UITableView滑動卡頓解決方案
阿新 • • 發佈:2019-01-05
UITableView是一個非常常用的基本檢視,在各類app中隨處可見。對於一般佈局簡單的tableView,效能上基本上看不出來什麼問題。但是對於cell中檢視繁多的tableView,有時候可能就會出現滑動不流暢的現象,以下是本人的一些解決方案,僅供參考。
1."讓出"主執行緒,讓主執行緒減負。所謂"讓出"主執行緒,指的是不要什麼操作都放在主執行緒裡。放在主執行緒中的一般都是檢視相關的操作,比如新增子檢視、更新子檢視、刪除子檢視等。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //處理一些跟當前檢視沒關係的事情 //... //只用來操作與當前檢視有關係的事情,比如:重新整理tableView dispatch_async(dispatch_get_main_queue(), ^{ [tableView reload]; }); });
2.正確重用cell。正確重用cell不僅僅要重用cell檢視,還需要好好重用cell的子檢視。
上面的程式碼在有cell可重用的時候,不會再建立新的cell,但是下面的一句話基本上會讓重用粉身碎骨。static NSString *Identifier = @"WeatherCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:<#(UITableViewCellStyle)#> reuseIdentifier:<#(NSString *)#>] }
//清空子檢視
[cell.contentView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[obj removeFromSuperview];
}];
上面這段程式碼之所以會出現,原因眾所周知:cell重用時會出現重疊。"解決"了重疊問題,那麼新問題來了,重新建立檢視既消耗記憶體還佔用時間,嚴重會出現滑動出現卡頓現象,而且都刪除了重建還能叫重用麼?舉例來說:
對於上面這樣的cell,雖然趕不上微博的複雜程度,但是子檢視數量也是挺多的,不合理的處理,會使得頁面滑動時有明顯示卡頓感。實現方案如下:
1.圖片實現非同步下載、非主執行緒。
//門店商品圖片描述
UIImageView *imageView = (UIImageView *)[cell.contentView viewWithTag:Merchant_Table_Cell_Desc_Image_View_Tag];
imageView.alpha = 1;
[imageView setImageWithDict:[NSDictionary dictionaryWithObject:merchant.imageName?merchant.imageName:@"" forKey:@"file_name"]];
此方法是UIImageView category中的一個方法,該方法的核心部分如下: //下載時讓出主執行緒
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//指示器
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
indicator.frame = CGRectMake((self.frame.size.width - 30)/2 , (self.frame.size.height - 30)/2, 30, 30);
[indicator startAnimating];
[self addSubview:indicator];
//待載入圖片的名稱(我的車庫圖片為:cars/A3.jpg格式)
NSString *fileName = [dict valueForKey:@"file_name"];
//快取檔案全路徑
NSString *filePath = [[datas objectForKey:@"documentPath"] stringByAppendingPathComponent:[fileName lastPathComponent]];
NSFileManager *manager = [NSFileManager defaultManager];
//下載檔案介面不會建立上級目錄,在呼叫之前建立資料夾路徑
if (![manager fileExistsAtPath:filePath isDirectory:NULL]) {
[manager createDirectoryAtPath:[datas objectForKey:@"documentPath"] withIntermediateDirectories:YES attributes:nil error:nil];
}
NSMutableDictionary *param = [[NSMutableDictionary alloc] init];
//下載的圖片名
[param setValue:[dict valueForKey:@"file_name"] forKey:@"file_name"];
//下載圖片儲存的位置
[param setValue:filePath forKey:@"filePath"];
//從網路獲取圖片
[VICBaseService downloadFileWithDictionary:param andSuccessBlock:^{
//操作檢視時,移除指示器、更新圖片,拿到主執行緒來做
dispatch_async(dispatch_get_main_queue(), ^{
[indicator removeFromSuperview];
//從本地快取中獲取
UIImage *image = [UIImage imageWithContentsOfFile:filePath]
if (!image) {
[self setDefaultImage:nil withCustomerViewMode:flag andContentMode:mode];
}else{
self.image = image;
[self customerImageViewMode:flag withContentMode:mode];
}
});
} andErrorBlock:^(NSError *error) {
//如果本地儲存了不完整的圖片,刪除圖片(不佔用主執行緒)
if ([manager fileExistsAtPath:filePath isDirectory:NULL]) {
[manager removeItemAtPath:filePath error:nil];
}
<pre name="code" class="objc"> //操作檢視時,移除指示器、更新圖片,拿到主執行緒來做
dispatch_async(dispatch_get_main_queue(), ^{
[indicator removeFromSuperview];
[self setDefaultImageWithImage:image withCustomerViewMode:flag andContentMode:mode];
});
} andProgressBlock:^(double progress) {
}];
});
2.重用cell並重用cell中的子檢視。新建一個UITableViewCell的子類,重寫下面的方法。
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
//門店商品圖片描述
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 87, 87)];
//設定子檢視的tag,方便重用時後能獲取
imageView.tag = Merchant_Table_Cell_Desc_Image_View_Tag;
[self.contentView addSubview:imageView];
//根據這個思路完成所有檢視的構建
}
在上述方法中,只設置基礎屬性,跟資料有關的屬性放在-tableView:cellForRowAtIndexPath:中去做。上圖中的"A"、"證"、"券"、"碼"。"A"跟商家等級走的,可能是B、C或者是D,所以不用在重寫的方法中實現該屬性,而"證"、"券"、"碼"可以直接寫死在自定義的cell中。
之前有說到重用cell時會遇到cell子檢視重疊的問題或者其它問題,問題的根源都是由於重用時子檢視的屬性不會被重置。對於重疊問題,思路如下:建立cell時儘可能的使cell包含所有的子檢視,並且在-tableView:cellForRowAtIndexPath:建立cell之後呼叫如下方法:
[cell.contentView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[obj setAlpha:0];
}];
不錯,我們不再是把子檢視移除,而是隱藏,我們只顯示我們需要的檢視,當前cell不需要的檢視,我們不用去動它,更不要刪除它,因為其它的cell如果會用到的話,就必須重新建立了,跟我們一開始的錯誤做法有什麼區別呢? UIImageView *imageView = (UIImageView *)[cell.contentView viewWithTag:Merchant_Table_Cell_Desc_Image_View_Tag];
imageView.alpha = 1;//只顯示我們需要的檢視
[imageView setImageWithDict:[NSDictionary dictionaryWithObject:merchant.imageName?merchant.imageName:@"" forKey:@"file_name"]];
這樣的話,我們重用cell容器,裡面的子檢視便不用重新建立,據測試,大量的建立再刪除子檢視會導致滑動具有明顯示卡頓感,並且有可能導致記憶體溢位。看看你的tableView是否也有這樣情況,時間允許的話,拆掉這個破窗戶吧。