提升 UITableView 性能

jopen 9年前發布 | 15K 次閱讀 iOS開發 移動開發 UITableView

原文  http://tutuge.me/2015/02/19/提升UITableView性能-復雜頁面的優化/

前言

隨著App的用戶界面的內容越來越豐富,再強的手機可能都無法同時渲染復雜的UI界面和保證流暢的體驗。所以,我們這些程序猿=。=在寫代碼的時候就要注意,如何盡可能提高用戶的操作流暢性。

之前的做的項目, 青桔音樂iOS客戶端 里面的首頁就是一個類似微信朋友圈的“動態”頁面,大致如下:

提升 UITableView 性能

如果是你,你會怎么實現這個頁面呢?

這還用問,當然是用UITableView+自定義的UITableViewCell。

UITableView是可以滑動的,為了不讓用戶在滑動中感到有卡頓,該如何優化?下面,我就寫一下我自己在做項目時的“經驗”~

優化

主要分為以下幾點:

  • 只定義一種Cell。
  • 提前計算并緩存每個Cell的高度。
  • 提前創建真正顯示的、需要加工的數據并緩存。
  • 緩存View!
  • 其它。

只定義一種Cell

乍一看,這個界面至少有3種樣式的Cell,為什么只定義一種呢?

分析結構

仔細分析一下,頁面中每個Cell的內容都有頭像、標題、正文、評論、其它(歌曲、圖片、歌手)。所以,從整體上看,每個Cell的結構是一致的!

重用=大致固定數量的Cell

并且,凡是認真研究過UITableView的人應該都知道,Apple已經為我們提供了Cell的重用,如用“ registerNib:forCellReuseIdentifier ”方法注冊自定義Cell的Nib,然后在“ cellForRowAtIndexPath ”的時候用“ dequeueReusableCellWithIdentifier ”獲取可以重用的Cell,所以,無論UITableView要顯示內容有多少,真正創建出的Cell可能只有5、6個。

所以,我們完全可以只創建一種Cell,雖然這樣一個Cell的“體積”可能會很大,但是介于Cell的數量不會很多,所以完全可以接受。

只定義一種Cell的好處

  • 減少代碼量,減少Nib文件的數量,統一一個Nib文件定義Cell,容易修改、維護。
  • 基于Cell的重用,真正運行時鋪滿屏幕所需的Cell數量大致是固定的,設為 N 個。所以如果如果只有一種Cell,那就是只有 N 個Cell的實例;但是如果有 M 種Cell,那么運行時最多可能會是“ M x N = MN ”個Cell的實例,雖然可能并不會占用太多內存,但是能少點不是更好嗎。

善用hidden隱藏(顯示)Subview

既然只定義一種Cell,那該如何顯示不同類型的內容呢?答案就是,把所有不同類型的view都定義好,放在cell里面,通過hidden顯示、隱藏,來顯示不同類型的內容。如下圖定義Cell:

提升 UITableView 性能

圖中的Subview1、Subview2、Subview3就是不同類型Cell的不同之處,所以我們在“ cellForRowAtIndexPath ”函數中,設置Cell的樣式、內容時,就可以通過顯示、隱藏這三個子view來顯示。

畢竟,在用戶快速滑動中,只是單純的顯示、隱藏subview比實時創建要快得多。

提前計算并緩存每個Cell的高度

開發過Android,用過Android的ListView以后,對UITableView需要提前計算Cell的高度很不適應。=。=

首先要確定的是,在iOS中,系統會先調用“ tableView:heightForRowAtIndexPath: ”獲取每個Cell即將顯示的高度,從而確定整個UITableView的布局。然后才調用“ tableView:cellForRowAtIndexPath ”獲取每個Cell,我們也是在這里填充、設置Cell的。

所以,既然高度總會被用到,那就早早的在獲取數據時就計算好吧!

在Model(Entity)中計算并保存Cell的高度

其實,在Model(Entity)中保存UI的參數是很奇怪的=。=(最好放在ViewModel中,就是MVVM模式的),我們的Entity可能就是下面的樣子:

@interface DataEntity : NSObject

//原始數據
@property(copy, nonatomic) NSString *content;
@property(copy, nonatomic) NSString *title;

//Cell 高度
@property(assign, nonatomic) CGFloat cellHeight;

//計算高度
- (void)calculateCellHeight;

@end

這樣,就不用在“ tableView:heightForRowAtIndexPath: ”中每次都計算了。

提前創建真正顯示的、需要加工的數據并緩存

Cell中顯示的內容,很多時候可能并不是直接從服務器拿到的數據,而是經過“加工”的數據。如本文中的“動態”也,每個Cell的標題、正文都有可點擊的連接Link、表情圖片等富文本內容,而我們一般用NSAttributeString類來顯示。

既然每次都會用到,倒不如在獲取到數據的時候就創建、加工好這些內容,等到需要現實的時候,直接拿來用不就行了。

所以,我們的Entity類可能變成下面這個樣子:

@interface DataEntity : NSObject

//原始數據
@property(copy, nonatomic) NSString *content;
@property(copy, nonatomic) NSString *title;

//Cell 高度
@property(assign, nonatomic) CGFloat cellHeight;

//真正顯示的內容
@property(strong, nonatomic) NSAttributedString *showTitle;
@property(strong, nonatomic) NSAttributedString *showContent;

//計算高度
- (void)calculateCellHeight;

//創建、加工真正顯示的內容
- (void)setupShowTitileAndContent;

@end

這樣,在“ tableView:cellForRowAtIndexPath ”中,我們直接拿showTitle、showContent來顯示就好,不用再創建。

緩存View!

什么?緩存View?!

是的,當Cell中的部分View是非常獨立的,并且不便于重用的,而且“體積”非常小,在內存可控的前提下,我們完全可以將這些view緩存起來!

方法當然也是將緩存的view放在Entity中~。

其它

當然,還有其他的優化方法,簡單說一說:

  • 盡量設置Cell的view為opaque,避免GPU對Cell下面的內容也進行繪制。
  • 避免大量的圖片縮放、顏色漸變等。
  • 避免同步的從網絡、文件獲取數據(這個是必須的=。=)
  • 用shadowPath創建陰影。
  • 盡量減少subview的數量,如多用drawRect繪制元素,替代用view顯示。
  • 盡量顯示“ 大小剛好合適 ”的圖片資源。

總結

總的來說,就是:

緩存一切可以緩存的!就是“用空間替換時間”!

在UITableView的Delegate、DataSource方法中,減少任何不必要的操作

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!