tableView 的性能優(yōu)化

tableView可以說是每個app中必不可少的控件,所以掌握流暢度優(yōu)化技能相當(dāng)?shù)闹匾?/p>


這里總結(jié)一些常用的優(yōu)化技巧,分享給大家:


① cell內(nèi)部控件的層次結(jié)構(gòu)盡量的少,可以使用drawRect畫;


② 控件盡量不要有透明度,因?yàn)槿绻蠈涌丶型该鞫鹊脑?,系統(tǒng)會努力的繪制下層控件的內(nèi)容與上層控件的內(nèi)容,并且將兩個內(nèi)容按照透明度去進(jìn)行繪制,十分耗性能;


③ 柵格化

將cell內(nèi)容渲染成一張圖片,在滾動的時候就是一張圖片:


layer.shouldRasterize? ? ? =? true;? // 柵格化cell,滾動時只顯示圖片

layer.rasterizationScale? =? [UIScreen mainScreen].scale;? // 默認(rèn)縮放比例是1,要適配當(dāng)前屏幕

在Instruments中調(diào)式可以看到cell已經(jīng)是黃色,說明已經(jīng)渲染成一張圖片:



柵格化cell.gif

④ 異步繪制cell的layer,如果cell比較復(fù)雜時可以使用


layer.drawsAsynchronously = true;

官方文檔注釋:

/* When true, the CGContext object passed to the -drawInContext: method

* may queue the drawing commands submitted to it, such that they will

* be executed later (i.e. asynchronously to the execution of the

* -drawInContext: method). This may allow the layer to complete its

* drawing operations sooner than when executing synchronously. The

* default value is NO. */

@property BOOL drawsAsynchronously

? CA_AVAILABLE_STARTING (10.8, 6.0, 9.0, 2.0);

意思就是如果使用異步,cell會提前繪制。

⑤ 在cell中不要用layer去畫圓角

CALayer的cornerRadius是一個超級費(fèi)性能的東西,它會在每一幀都裁剪圓角,無論你有沒有滾動視圖都會運(yùn)算裁剪圓角,很費(fèi)GPU性能!所以要讓CPU去做圓角圖片!

可以在UIImage分類中,開啟上下文,利用貝塞爾路徑畫圓角:


#import "UIImage+Extension.h"


@implementation UIImage (Extension)


- (void)qv_cornerImageWithSize:(CGSize)size fillColor:(UIColor *)fillColor completion:(void (^)(UIImage *))completion {


? ? dispatch_async(dispatch_get_global_queue(0, 0), ^{

? ? ? ? UIGraphicsBeginImageContextWithOptions(size, true, 0);

? ? ? ? CGRect rect = CGRectMake(0, 0, size.width, size.height);

? ? ? ? [fillColor setFill];

? ? ? ? UIRectFill(rect);

? ? ? ? UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];

? ? ? ? [path addClip];

? ? ? ? [self drawInRect:rect];

? ? ? ? UIImage *result = UIGraphicsGetImageFromCurrentImageContext();

? ? ? ? UIGraphicsEndImageContext();

? ? ? ? dispatch_async(dispatch_get_main_queue(), ^{

? ? ? ? ? ? if (result != nil) {

? ? ? ? ? ? ? ? completion(result);

? ? ? ? ? ? }

? ? ? ? });

? ? });

}


@end

參考文章

http://www.cocoachina.com/ios/20150803/12873.html

http://blog.csdn.net/shaobo8910/article/details/46779259

也可以讓服務(wù)器去處理圓角圖片,這樣我們就不需要再去操作。

注意:iOS9.0之后,.png圖片直接設(shè)置圓角是不會產(chǎn)生離屏渲染,iOS9.0之前還是會離屏渲染的。


⑥ 緩存行高

如果是自動布局計算行高很消耗CPU,每次滾動到該cell都要計算self.contentView.layoutIfNeeded,注意要移除contentView的底部約束。建議復(fù)雜的cell不要用自動布局。


⑦ cell內(nèi)部所有顯示的數(shù)據(jù)提前準(zhǔn)備好,盡量少實(shí)時計算。所有的控件大小提前計算好,不要每一次都計算。


⑧ 按需加載,按照用戶滾動的速度去選擇加載哪個cell

原理:在快速滑動松手后滾動的cell個數(shù)超過預(yù)定的個數(shù),只顯示最后出現(xiàn)的cell的前三個cell,把這三個cell的indexPath存到數(shù)組中,在數(shù)據(jù)源方法里判斷如果數(shù)組count>0,且數(shù)組不包含當(dāng)前的indexPath,那就說明此cell是在快速滑動中需要隱藏的:

代理方法:


//按需加載 - 如果目標(biāo)行與當(dāng)前行相差超過指定行數(shù),只在目標(biāo)滾動范圍的前后指定3行加載。

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{

? ? NSIndexPath *ip = [_titleTableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];

? ? NSIndexPath *cip = [[_titleTableView indexPathsForVisibleRows] firstObject];

? ? NSInteger skipCount = 1;? ? // 這里我為了方便演示寫的1,大家可以按需求自行設(shè)定

? ? if (labs(cip.row-ip.row)>skipCount) {

//? ? ? ? 此方法可以獲取將要顯示的組

//? ? ? ? visibleSections = [NSSet setWithArray:[[_titleTableView indexPathsForVisibleRows] valueForKey:@"section"]];

? ? ? ? NSArray *temp = [_titleTableView indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, _titleTableView.frame.size.width, _titleTableView.frame.size.height)];

? ? ? ? NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];

? ? ? ? if (velocity.y<0) {? ? ? // 上滑

? ? ? ? ? ? NSIndexPath *indexPath = [temp lastObject];

? ? ? ? ? ? if (indexPath.row+3<numberOfRows*numberOfSections) {

? ? ? ? ? ? ? ? [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+3 inSection:indexPath.section]];

? ? ? ? ? ? ? ? [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+2 inSection:indexPath.section]];

? ? ? ? ? ? ? ? [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section]];

? ? ? ? ? ? }

? ? ? ? } else {? ? ? ? ? ? ? ? // 下滑

? ? ? ? ? ? NSIndexPath *indexPath = [temp firstObject];

? ? ? ? ? ? if (indexPath.row>3) {

? ? ? ? ? ? ? ? [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:indexPath.section]];

? ? ? ? ? ? ? ? [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:indexPath.section]];

? ? ? ? ? ? ? ? [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section]];

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? [needLoadArr addObjectsFromArray:arr];

? ? }

}

相應(yīng)的,每次開始拖動的時候去清空數(shù)組。還有種情況,如果界面上有顯示空白cell的時候突然手動停止?jié)L動呢?


- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

? ? [needLoadArr removeAllObjects];? ? // 清空數(shù)組

? ? // 取到當(dāng)前界面上能顯示的indexPaths,判斷是否有隱藏

? ? NSArray <NSIndexPath *>*indexpaths = [_titleTableView indexPathsForVisibleRows];

? ? UITableViewCell *firstCell? =? [_titleTableView cellForRowAtIndexPath:indexpaths.firstObject];

? ? UITableViewCell *lastCell? =? [_titleTableView cellForRowAtIndexPath:indexpaths.lastObject];

? ? //? 在當(dāng)前可見的區(qū)域中,第一個cell或者最后一個cell是隱藏狀態(tài),那么重新加載可見區(qū)域內(nèi)的cell

? ? if (firstCell.isHidden == true || lastCell.isHidden == true) {

? ? ? ? [_titleTableView reloadRowsAtIndexPaths:indexpaths withRowAnimation:UITableViewRowAnimationNone];

? ? }

}

也可以把判斷的代碼寫在scrollView停止?jié)L動監(jiān)聽方法里,但是個人覺得沒必要,因?yàn)檫@種情況必定是手動觸碰去停止的,這里處理沒問題

數(shù)據(jù)源方法:


? if (needLoadArr.count > 0) {

? ? ? ? if (![needLoadArr containsObject:indexPath]) {

//? ? ? ? ? ? NSLog(@"該cell是快速滑動中的cell,所以隱藏");

? ? ? ? ? ? cell.hidden = true;

? ? ? ? ? ? return cell;

? ? ? ? }

? ? }

? ? cell.hidden = false;? ? ? // 正常顯示的cell

按需加載參考demo:https://github.com/johnil/VVeboTableViewDemo 這位前輩在tableView優(yōu)化上做到了極致


⑨ 其他:

盡量少的使用富文本;

時間格式化對象使用同一個;


總結(jié):tableView性能優(yōu)化的方式有很多,但不是所有的我們都需要。比如不是必需要顯示的界面,預(yù)先計算行高就是浪費(fèi)。我們開發(fā)者應(yīng)當(dāng)結(jié)合實(shí)際情況,從用戶的角度出發(fā),這是做一個優(yōu)秀App最基本也是最核心的思想。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容