tableView 的性能優(yōu)化

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最基本也是最核心的思想。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捕传,死亡現(xiàn)場離奇詭異惠拭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門职辅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棒呛,“玉大人,你說我怎么就攤上這事域携〈孛耄” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵秀鞭,是天一觀的道長趋观。 經(jīng)常有香客問我,道長锋边,這世上最難降的妖魔是什么皱坛? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮豆巨,結(jié)果婚禮上剩辟,老公的妹妹穿的比我還像新娘。我一直安慰自己往扔,他們只是感情好贩猎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著萍膛,像睡著了一般吭服。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卦羡,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天噪馏,我揣著相機(jī)與錄音麦到,去河邊找鬼绿饵。 笑死,一個胖子當(dāng)著我的面吹牛瓶颠,可吹牛的內(nèi)容都是我干的拟赊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼粹淋,長吁一口氣:“原來是場噩夢啊……” “哼吸祟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桃移,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤屋匕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后借杰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體过吻,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纤虽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乳绕。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖逼纸,靈堂內(nèi)的尸體忽然破棺而出洋措,到底是詐尸還是另有隱情,我是刑警寧澤杰刽,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布菠发,位于F島的核電站,受9級特大地震影響专缠,放射性物質(zhì)發(fā)生泄漏雷酪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一涝婉、第九天 我趴在偏房一處隱蔽的房頂上張望哥力。 院中可真熱鬧,春花似錦墩弯、人聲如沸吩跋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锌钮。三九已至,卻和暖如春引矩,著一層夾襖步出監(jiān)牢的瞬間梁丘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工旺韭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氛谜,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓区端,卻偏偏與公主長得像值漫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子织盼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容