IOS之UITableView性能優(yōu)化

前言

????????說起優(yōu)化,簡直是博大精深诽凌。話不多說矾瑰,筆者今天梳理的內(nèi)容,UITableView的性能優(yōu)化酵镜。先說一下tableview的執(zhí)行順序:

1.它會調(diào)用代理方法確定有幾個分區(qū)

numberOfSectionsInTableView:

2.確定每個分區(qū)的表頭高和表尾高(如果設(shè)定了HeardView和FooterView)

heightForHeaderInSection:

tableView:heightForFooterInSection:

3.確定每個分區(qū)有多少的cell

numberOfRowsInSection:

4.然后確定cell的高度

heightForRowAtIndexPath:

如果有多個section和row則循環(huán)執(zhí)行上面的代碼

5.以上信息確定完畢后及調(diào)用代理方法去獲取cell

cellForRowAtIndexPath:

6.返回cell的高度

heightForRowAtIndexPath:

7.cell將要顯示到屏幕上

willDisplayCell:forRowAtIndexPath:

8.cell超出屏幕進行服用時及會調(diào)用兩次

heightForRowAtIndexPath:

然后在進行調(diào)用 5 . 6. 7 方法

一碉碉、cell的復(fù)用

? ? ????首先cell有兩種復(fù)用方法:

????????????????- (id)dequeueReusableCellWithIdentifier:(NSString?*)identifier; ?

????????????????-?(id)dequeueReusableCellWithIdentifier:(NSString?*)identifier?forIndexPath:(NSIndexPath?*)indexPath; ?

????????在iOS 6中dequeueReusableCellWithIdentifier:被dequeueReusableCellWithIdentifier:forIndexPath:所取代。如此一來淮韭,在表格視圖中創(chuàng)建并添加UITableViewCell對象會變得更為精簡而流暢垢粮。而且使用dequeueReusableCellWithIdentifier:forIndexPath:一定會返回cell,系統(tǒng)在默認沒有cell可復(fù)用的時候會自動創(chuàng)建一個新的cell出來靠粪。

1.dequeueReusableCellWithIdentifier:(NSString?*)identifier,如圖:


2.dequeueReusableCellWithIdentifier:(NSString?*)identifier?forIndexPath:(NSIndexPath?*)indexPath蜡吧,如圖:

????????獲取cell時如果沒有可重用cell粱腻,如果cell為nib,將創(chuàng)建新的cell并調(diào)用其中的awakeFromNib方法斩跌;否則

調(diào)用cell中的initWithStyle:withReuseableCellIdentifier:方法創(chuàng)建新的cell。不過此方法捞慌,在創(chuàng)建tableview的時候耀鸦,要注冊cell,如圖:


? ? ? ? 不論以上哪兩種方法啸澡,我們獲取到cell后袖订,可能習慣在cellForRowAtIndexPath:中為每一個cell綁定數(shù)據(jù),實際上在調(diào)用cellForRowAtIndexPath:的時候cell還沒有被顯示出來嗅虏,為了提高效率我們應(yīng)該把數(shù)據(jù)綁定的操作放在cell顯示出來后再執(zhí)行洛姑,可以在tableView:willDisplayCell:forRowAtIndexPath:(以后簡稱willDisplayCell)方法中綁定數(shù)據(jù)。

????????*注意*:

????????willDisplayCell在cell 在tableview展示之前就會調(diào)用皮服,此時cell實例已經(jīng)生成楞艾,所以不能更改cell的結(jié)構(gòu),只能是改動cell上的UI的一些屬性(例如label的內(nèi)容等)龄广。


二硫眯、cell的高度

????????說到cell高度優(yōu)化問題,可能大家都知道去計算并緩存cell的高度择同。今天說另一種方法两入。我們分為兩種cell,一種是定高的cell敲才,另外一種是動態(tài)高度的cell裹纳。

????????(1)定高的cell,應(yīng)該采用如下方式:

????????self.tableView.rowHeight = 100;

????????這個方法指定了所有cell高度都是100的tableview紧武,rowHeight默認的值是44剃氧,所以一個空的TableView會顯示成這個樣子。對于定高cell阻星,直接采用上面方式給定高度她我,不需要實現(xiàn)tableView:heightForRowAtIndexPath:以節(jié)省不必要的計算和開銷。

????????(2)動態(tài)高度的cell

????????我們需要實現(xiàn)它的代理迫横,來給出高度:

????????-(CGFloat)tableView:(UITableView*)tableViewheightForRowAtIndexPath:(NSIndexPath*)indexPath{

????????????????//return xxx;

????????}

????????這個代理方法實現(xiàn)后番舆,上面的rowHeight的設(shè)置將會變成無效。在這個方法中矾踱,我們需要提高cell高度的計算效率恨狈,來節(jié)省時間。自從iOS8之后有了Self-Sizing cell的概念呛讲,cell可以自己算出高度禾怠,IOS11以后返奉,Self-Sizing默認開啟,包括Headers, footers吗氏。

????????使用self-sizing cell需要滿足以下三個條件:

????????(1)使用Autolayout進行UI布局約束(要求cell.contentView的四條邊都與內(nèi)部元素有約束關(guān)系)芽偏。

????????(2)指定TableView的estimatedRowHeight屬性的默認值。

????????(3)指定TableView的rowHeight屬性為UITableViewAutomaticDimension弦讽。

????????- (void)viewDidload {

????????????self.myTableView.estimatedRowHeight = 44.0;

????????????self.myTableView.rowHeight = UITableViewAutomaticDimension;

????????}

三污尉、cell的渲染

????????為了保證TableView的流暢,當快速滑動的時候往产,cell必須被快速的渲染出來被碗。所以cell渲染的速度必須快。如何提高cell的渲染速度呢仿村?

????????(1)當有圖像時锐朴,預(yù)渲染圖像,在bitmap context先將其畫一遍蔼囊,導(dǎo)出成UIImage對象焚志,然后再繪制到屏幕,這會大大提高渲染速度畏鼓。具體內(nèi)容可以自行查找“利用預(yù)渲染加速顯示iOS圖像”相關(guān)資料

????????(2)渲染最好時的操作之一就是混合(blending)了,所以我們不要使用透明背景娩嚼,將cell的opaque值設(shè)為Yes,背景色不要使用clearColor滴肿,盡量不要使用陰影漸變等岳悟;

????????(3)由于混合操作是使用GPU來執(zhí)行,我們可以用CPU來渲染泼差,這樣混合操作就不再執(zhí)行贵少。可以在UIView的drawRect方法中自定義繪制堆缘;

????????(4)減少subviews的個數(shù)和層級滔灶。子控件的層級越深,渲染到屏幕上所需要的計算量就越大吼肥;如多用drawRect繪制元素录平,替代用view顯示;

????????(5)少用subviews的透明圖層缀皱。對于不透明的View斗这,設(shè)置opaque為YES,這樣在繪制該View時啤斗,就不需要考慮被View覆蓋的其他內(nèi)容(盡量設(shè)置Cell的view為opaque表箭,避免GPU對Cell下面的內(nèi)容也進行繪制);

?????????(6)避免CALayer特效(shadowPath)钮莲。 給Cell中View加陰影會引起性能問題免钻,如下面代碼會導(dǎo)致滾動時有明顯的卡頓:

????????view.layer.shadowColor= color.CGColor;

????????view.layer.shadowOffset= offset;

????????view.layer.shadowOpacity=1;

????????view.layer.shadowRadius= radius;

????????(7)我們在cell上添加系統(tǒng)控件的時候彼水,實際上系統(tǒng)都會調(diào)用底層的接口進行繪制,大量添加控件時极舔,會消耗很大的資源并且也會影響渲染的性能凤覆。當使用默認的UITableViewCell并且在它的ContentView上面添加控件時會相當消耗性能。所以目前最佳的方法還是繼承UITableViewCell拆魏,并重寫drawRect方法盯桦。

????????(8)在實現(xiàn)drawRect方法的時候,它的參數(shù)rect就是我們需要繪制的區(qū)域稽揭,在rect范圍之外的區(qū)域我們不需要進行繪制,否則會消耗相當大的資源肥卡。

????????(9)不要給cell動態(tài)添加subView,在初始化cell的時候就將所有需要展示的添加完畢溪掀,然后根據(jù)需要來設(shè)置hide屬性顯示和隱藏。

????????(10)異步化UI步鉴,不要阻塞主線程.

? ? ? ? (11)滑動時按需加載對應(yīng)的內(nèi)容

? ? ? ? 參考文章

四揪胃、離屏渲染

????????下面的情況或操作會引發(fā)離屏渲染:

? ? ? ? (1)、為圖層設(shè)置遮罩(layer.mask)氛琢;

? ? ? ? (2)喊递、將圖層的layer.masksToBounds / view.clipsToBounds屬性設(shè)置為true;

? ? ? ? (3)阳似、將圖層layer.allowsGroupOpacity屬性設(shè)置為YES和layer.opacity小于1.0骚勘;

? ? ? ? (4)、為圖層設(shè)置陰影(layer.shadow *)撮奏;

? ? ? ? (5)俏讹、為圖層設(shè)置layer.shouldRasterize=true;

? ? ? ? (6)畜吊、具有l(wèi)ayer.cornerRadius泽疆,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的圖層玲献;

? ? ? ? (7)殉疼、文本(任何種類,包括UILabel捌年,CATextLayer瓢娜,Core Text等);

? ? ? ? (8)礼预、使用CGContext在drawRect :方法中繪制大部分情況下會導(dǎo)致離屏渲染恋腕,甚至僅僅是一個空的實現(xiàn)。

1逆瑞、優(yōu)化方案

????????官方對離屏渲染產(chǎn)生性能問題也進行了優(yōu)化:

????????iOS 9.0 之前UIimageView跟UIButton設(shè)置圓角都會觸發(fā)離屏渲染荠藤。iOS 9.0 之后UIButton設(shè)置圓角會觸發(fā)離屏渲染伙单,而UIImageView里png圖片設(shè)置圓角不會觸發(fā)離屏渲染了,如果設(shè)置其他陰影效果之類的還是會觸發(fā)離屏渲染的哈肖。

優(yōu)化方案1:使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個圓角

????????UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

????????imageView.image = [UIImage imageNamed:@"myImg"];

????????//開始對imageView進行畫圖

????????UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);

????????//使用貝塞爾曲線畫出一個圓形圖

????????[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];

????????[imageView drawRect:imageView.bounds];

????????imageView.image = UIGraphicsGetImageFromCurrentImageContext();

????????//結(jié)束畫圖

????????UIGraphicsEndImageContext();

????????[self.view addSubview:imageView];

優(yōu)化方案2:使用CAShapeLayer和UIBezierPath設(shè)置圓角

????????UIImageView *imageView=[[UIImageViewalloc]initWithFrame:CGRectMake(100,100,100,100)];

????????imageView.image=[UIImageimageNamed:@"myImg"];

????????UIBezierPath *maskPath=[UIBezierPathbezierPathWithRoundedRect:imageView.boundsbyRoundingCorners:UIRectCornerAllCornerscornerRadii:imageView.bounds.size];

????????CAShapeLayer *maskLayer=[[CAShapeLayeralloc]init];

????????//設(shè)置大小

????????maskLayer.frame=imageView.bounds;

????????//設(shè)置圖形樣子

????????maskLayer.path=maskPath.CGPath;

????????imageView.layer.mask=maskLayer;

????????[self.viewaddSubview:imageView];

????????對于方案2需要解釋的是:

????????CAShapeLayer繼承于CALayer,可以使用CALayer的所有屬性值吻育;CAShapeLayer需要貝塞爾曲線配合使用才有意義(也就是說才有效果);使用CAShapeLayer(屬于CoreAnimation)與貝塞爾曲線可以實現(xiàn)不在view的drawRect(繼承于CoreGraphics走的是CPU,消耗的性能較大)方法中畫出一些想要的圖形,CAShapeLayer動畫渲染直接提交到手機的GPU當中,相較于view的drawRect方法使用CPU渲染而言淤井,其效率極高布疼,能大大優(yōu)化內(nèi)存使用情況”液荩總的來說就是用CAShapeLayer的內(nèi)存消耗少游两,渲染速度快,建議使用優(yōu)化方案2漩绵。

(2)shadow優(yōu)化

????????對于shadow贱案,如果圖層是個簡單的幾何圖形或者圓角圖形,我們可以通過設(shè)置shadowPath來優(yōu)化性能止吐,能大幅提高性能宝踪。示例如下:

????????imageView.layer.shadowColor=[UIColorgrayColor].CGColor;

????????imageView.layer.shadowOpacity=1.0;

????????imageView.layer.shadowRadius=2.0;

????????UIBezierPath *path=[UIBezierPathbezierPathWithRect:imageView.frame];

????????imageView.layer.shadowPath=path.CGPath;

????????我們還可以通過設(shè)置shouldRasterize屬性值為YES來強制開啟離屏渲染。其實就是光柵化(Rasterization)碍扔。既然離屏渲染這么不好瘩燥,為什么我們還要強制開啟呢?當一個圖像混合了多個圖層不同,每次移動時厉膀,每一幀都要重新合成這些圖層,十分消耗性能二拐。當我們開啟光柵化后站蝠,會在首次產(chǎn)生一個位圖緩存,當再次使用時候就會復(fù)用這個緩存卓鹿。但是如果圖層發(fā)生改變的時候就會重新產(chǎn)生位圖緩存菱魔。所以這個功能一般不能用于UITableViewCell中,cell的復(fù)用反而降低了性能吟孙。最好用于圖層較多的靜態(tài)內(nèi)容的圖形澜倦。而且產(chǎn)生的位圖緩存的大小是有限制的,一般是2.5個屏幕尺寸杰妓。在100ms之內(nèi)不使用這個緩存藻治,緩存也會被刪除。所以我們要根據(jù)使用場景而定巷挥。

(3)其他的一些優(yōu)化建議

? ? ? ? 1.當我們需要圓角效果時桩卵,可以使用一張中間透明圖片蒙上去

? ? ? ? 2.使用ShadowPath指定layer陰影效果路徑

? ? ? ? 3.使用異步進行l(wèi)ayer渲染(Facebook開源的異步繪制框架AsyncDisplayKit)

? ? ? ? 4.設(shè)置layer的opaque值為YES,減少復(fù)雜圖層合成

? ? ? ? 5.盡量使用不包含透明(alpha)通道的圖片資源

? ? ? ? 6. 盡量設(shè)置layer的大小值為整形值

? ? ? ? 7.直接讓美工把圖片切成圓角進行顯示,這是效率最高的一種方案

? ? ? ? 8.很多情況下用戶上傳圖片進行顯示雏节,可以讓服務(wù)端處理圓角

? ? ? ? 9.使用代碼手動生成圓角Image設(shè)置到要顯示的View上胜嗓,利用UIBezierPath(CoreGraphics框架)畫出來圓角圖片

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钩乍,隨后出現(xiàn)的幾起案子辞州,更是在濱河造成了極大的恐慌,老刑警劉巖寥粹,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件变过,死亡現(xiàn)場離奇詭異,居然都是意外死亡涝涤,警方通過查閱死者的電腦和手機媚狰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阔拳,“玉大人崭孤,你說我怎么就攤上這事∩郎” “怎么了裳瘪?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵土浸,是天一觀的道長罪针。 經(jīng)常有香客問我,道長黄伊,這世上最難降的妖魔是什么泪酱? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮还最,結(jié)果婚禮上墓阀,老公的妹妹穿的比我還像新娘。我一直安慰自己拓轻,他們只是感情好斯撮,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扶叉,像睡著了一般勿锅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枣氧,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天溢十,我揣著相機與錄音,去河邊找鬼达吞。 笑死张弛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吞鸭,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼寺董,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞒大?” 一聲冷哼從身側(cè)響起螃征,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎透敌,沒想到半個月后盯滚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡酗电,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年魄藕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撵术。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡背率,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嫩与,到底是詐尸還是另有隱情寝姿,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布划滋,位于F島的核電站饵筑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏处坪。R本人自食惡果不足惜根资,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望同窘。 院中可真熱鬧玄帕,春花似錦、人聲如沸想邦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丧没。三九已至鹰椒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間骂铁,已是汗流浹背吹零。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拉庵,地道東北人灿椅。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茫蛹。 傳聞我的和親對象是個殘疾皇子操刀,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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