tableView可以說是每個app中必不可少的控件,所以掌握流暢度優(yōu)化技能相當(dāng)?shù)闹匾?/p>
這里總結(jié)一些常用的優(yōu)化技巧盖矫,分享給大家:
① cell內(nèi)部控件的層次結(jié)構(gòu)盡量的少丽惭,可以使用drawRect畫;
② 控件盡量不要有透明度辈双,因?yàn)槿绻蠈涌丶型该鞫鹊脑捲鹛停到y(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之前還是會離屏渲染的掐禁。
⑥ 緩存行高
如果是自動布局計(jì)算行高很消耗CPU,每次滾動到該cell都要計(jì)算self.contentView.layoutIfNeeded蹭越,注意要移除contentView的底部約束。建議復(fù)雜的cell不要用自動布局买置。
⑦ cell內(nèi)部所有顯示的數(shù)據(jù)提前準(zhǔn)備好,盡量少實(shí)時計(jì)算倦卖。所有的控件大小提前計(jì)算好,不要每一次都計(jì)算秦踪。
⑧ 按需加載褐捻,按照用戶滾動的速度去選擇加載哪個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ù)先計(jì)算行高就是浪費(fèi)滔韵。我們開發(fā)者應(yīng)當(dāng)結(jié)合實(shí)際情況,從用戶的角度出發(fā),這是做一個優(yōu)秀App最基本也是最核心的思想。