本文為純手打,內(nèi)容取自南峰子老師的博客:http://southpeak.github.io/
里面有些地方?jīng)]有實(shí)踐,也不是很理解,大家共同學(xué)習(xí)旱函。
內(nèi)建方法:
首先是重用cell/header/footer的單個(gè)實(shí)例,幾遍是我們需要顯示多個(gè).這是優(yōu)化UIScrollerView最明顯的方式,為了正確的使用它,你應(yīng)該只有cell/header/footer類,一次性初始化他們,并返回給UITableView.
蘋果開發(fā)文檔里有重用cell的流程.
重要的事情是:在UITableView的dataSource中實(shí)現(xiàn)的tableView:cellForRowAtIndexPath:方法,需要為每個(gè)cell調(diào)用一次,他應(yīng)該快速執(zhí)行.所以需要盡可能快地返回重用cell實(shí)例.不要在這里進(jìn)行數(shù)據(jù)綁定,因?yàn)槟壳霸谄聊簧线€沒(méi)有cell.為了執(zhí)行數(shù)據(jù)綁定可以在UITableView的delegate方法tableView:willDisplayCell:forRowAtIndexPath:中進(jìn)行.這個(gè)方法在顯示之前會(huì)被調(diào)用.
這個(gè)方法對(duì)于cell定高的UITableView來(lái)說(shuō)沒(méi)有意義,但如果由于某些原因需要?jiǎng)討B(tài)高度的cell的話,這個(gè)方法可以很容易地讓滑動(dòng)更流暢.
正如我們所知,UITableView是UIScrollerView的子類,而UIScrollerView的作用是讓用戶可以與比屏幕尺寸更大的區(qū)域交互.任何UIScrollerView的實(shí)例都是用諸如contentSizew、contentOffset和其他許多屬性來(lái)將正確的區(qū)域顯示給用戶.
但是UITableView得問(wèn)他在哪描滔?正如所解釋的一樣棒妨,UITableView不會(huì)同時(shí)維護(hù)所有cell的實(shí)例。相反他只需要維護(hù)顯示給用戶的那些cell含长。
那么券腔,UITableView是如何知道她的contentSize呢?他是通過(guò)計(jì)算所有cell的高度之和來(lái)計(jì)算contentSize的值拘泞。
UITableView的delegate方法tableView:heightForRowAtIndexPath:會(huì)為每個(gè)cell調(diào)用一次纷纫,所以你應(yīng)該非常快地返回高度值陪腌。
很多人會(huì)犯一個(gè)錯(cuò)誤辱魁,他們會(huì)在布局初始化cell實(shí)例并綁定數(shù)據(jù)后獲取他們的高度烟瞧。如果你想優(yōu)化滑動(dòng)的性能,就不應(yīng)該以這種方式來(lái)計(jì)算cell的高度染簇,因?yàn)檫@事難以置信的低效参滴,iOS設(shè)備標(biāo)準(zhǔn)的60FPS將會(huì)降低到15-20FPS,滑動(dòng)會(huì)變得很慢锻弓。
如果我們沒(méi)有一個(gè)cell的實(shí)例砾赔,那如何計(jì)算他的高度呢?
它使用類方法青灼,并基于傳入的寬度及顯示的數(shù)據(jù)來(lái)計(jì)算高度值
從iOS8 開始暴心,我們可以在UITableView的delegate中使用自動(dòng)高度計(jì)算,而不需要實(shí)現(xiàn)上面提到的方法杂拨。為了實(shí)現(xiàn)這一功能专普,你可能會(huì)使用AutoLayout,并將rowHeight變量設(shè)置為UITableViewAutomaticDimension扳躬〈嗨撸可以在StackOverflow中找到更多詳細(xì)信息甚亭。
如果你想讓你的App在所有設(shè)備上都能平滑的滾動(dòng)贷币,你會(huì)發(fā)現(xiàn)這種方法難以置信的慢,你使用的子視圖越多亏狰,AutoLayout的效率越低役纹,原因是隱藏在底層的命名為“Cassowary”的約束求解系統(tǒng)。如果布局中子視圖越多暇唾,那么需要求解的約束也越多促脉,進(jìn)而返回cell給UITableView所花的時(shí)間也越多。
使用內(nèi)建方法優(yōu)化UITableView的正確方法是:
重用cell實(shí)例:對(duì)于特殊類型的cell策州,你應(yīng)該只有一個(gè)實(shí)例瘸味,而沒(méi)有更多。
不要在cellForRowAtIndexPath:方法中綁定數(shù)據(jù)够挂,因?yàn)樵诖藭r(shí)cell還沒(méi)有顯示旁仿。可以使用UITableView的delegate中的tableView:willDisplayCell:forRowAtIndexPath:方法孽糖。
快速計(jì)算cell高度枯冈。
更深一步
上面提到的這些點(diǎn)不足以實(shí)現(xiàn)真正的平滑滾動(dòng),特別是當(dāng)你需要實(shí)現(xiàn)一些復(fù)雜的cell(如有大量的簡(jiǎn)便办悟、視圖尘奏、交互元素、一些修飾元素等等)時(shí)病蛉,這變得尤其明顯炫加。
這種情況下瑰煎,UITableView很容易變得緩慢,即便是做了上面所有的事情琢感。UITableViewCell中的視圖越多丢间,滑動(dòng)時(shí)FPS越低。但在使用了手動(dòng)布局優(yōu)化了高度計(jì)算后驹针,問(wèn)題就不在布局了烘挫,而在渲染了。
讓我們把關(guān)注點(diǎn)放在UIView的opaque上屬性上柬甥,文檔中說(shuō)它用于輔助繪圖系統(tǒng)定義UIView是否透明饮六,如果不透明,則繪圖系統(tǒng)在渲染視圖時(shí)可以做一些優(yōu)化苛蒲,以提高性能卤橄。
我們需要性能,或者不是臂外?用戶可能快速地滑動(dòng)table窟扑,如使用scrollsToTop特性,但他們可能沒(méi)有最新的iPhone漏健,所以cell必須快速的被渲染嚎货。比通常的視圖更快。
渲染最慢的操作之一是混合(blending)蔫浆≈呈簦混合操作由GPU來(lái)執(zhí)行,因?yàn)檫@個(gè)硬件就是用來(lái)做混合操作的(當(dāng)然不只是混合)瓦盛。
你可能已經(jīng)猜到提高性能的方法是減少混合操作的次數(shù)洗显,在此之前,我們需要找到它原环。
在模擬器上運(yùn)行APP挠唆,在模擬器的菜單中選擇‘Debug’,然后選中‘Color Blended Layers’嘱吗。然后模擬器就會(huì)將全部區(qū)域顯示為兩種顏色:綠色和紅色玄组。(綠色沒(méi)有混合,紅色區(qū)域表示有混合操作)每種情況都應(yīng)該仔細(xì)研究柜与,不同的情況需要使用不同的方法來(lái)避免混合巧勤。一般做法是設(shè)置backgroundColor來(lái)實(shí)現(xiàn)非透明。
但有時(shí)候可能更復(fù)雜弄匕。有一個(gè)漸變颅悉,但是沒(méi)有混合。
這確實(shí)發(fā)生了,因?yàn)槲覀兓旌狭藘蓚€(gè)不同層的內(nèi)容:UILabel的CATextLayer和我們的CAGradientLayer延曙。
所以需要怎么做呢?事實(shí)上愿卸,解決的方案是:使用CPU來(lái)渲染灵临!這將不會(huì)加載GPU,這樣就無(wú)法執(zhí)行混合操作趴荸,例如儒溉,在執(zhí)行動(dòng)畫的CALayer上。
我們可以在UIView的drawRect:方法中使用CoreGraphics操作來(lái)執(zhí)行CPU渲染发钝,
通過(guò)這種方式顿涣,你會(huì)撤銷在一些UIView上(在任何情況下,他們都是不必要的)的所有緩存優(yōu)化操作酝豪。但是涛碑,這種方法禁用了一些混合操作,卸載GPU寓调,從而使UITableView更順暢锌唾。
但是記壮搿:這提高了渲染性能夺英,不是因?yàn)镃PU比GPU更快,他可以讓我們通過(guò)為讓CPU來(lái)執(zhí)行某些渲染任務(wù)滋捶,從而卸載GPU痛悯,因?yàn)樵诤芏嗲闆r下,CPU可能不是100%負(fù)載的重窟。優(yōu)化混合操作的關(guān)鍵點(diǎn)是在平衡CPU和GPU的負(fù)載载萌。(這個(gè)CoreGraphics好難理解感覺(jué)!)
優(yōu)化UITableView中繪制數(shù)據(jù)操作的小結(jié):
減少iOS執(zhí)行無(wú)用混合的區(qū)域:不要使用透明背景巡扇,使用模擬器或者Instruments來(lái)確認(rèn)這一點(diǎn)扭仁,如果可以盡量使用沒(méi)有混合的漸變。
優(yōu)化代碼厅翔,以平衡CPU和GPU的負(fù)載乖坠。你需要清楚的知道那部分渲染需要使用GPU,那部分可以使用CPU刀闷,以此保持平衡熊泵。
為特殊的cell類型編寫特殊的代碼仰迁。
像素獲取
自從有了Retina屏后,在Cocoa Touch環(huán)境下顽分,我們就可以用屏幕點(diǎn)來(lái)取代像素了徐许,同時(shí)屏幕點(diǎn)可以是浮點(diǎn)值。
現(xiàn)實(shí)生活中它可能是浮點(diǎn)值卒蘸,例如雌隅,線段可能起始于x為0.25的地方。這時(shí)候缸沃,iOS將執(zhí)行子像素渲染澄步。這一技術(shù)在應(yīng)用于特定類型的內(nèi)容是很有意義。但當(dāng)我們繪制平滑直線時(shí)則沒(méi)有必要和泌。
如果所有的平滑線段都使用子像素渲染技術(shù)來(lái)渲染村缸,那會(huì)讓你iOS執(zhí)行一些不必要的任務(wù),從而降低FPS武氓。
什么情況下會(huì)出現(xiàn)這種不必要的子像素抗鋸齒操作呢梯皿?最常發(fā)生的情況是通過(guò)代碼計(jì)算而變成浮點(diǎn)值的視圖坐標(biāo),或者是一些不正常的圖片資源县恕,這些圖片的大小不是對(duì)起到屏幕的物理像素上的(例如东羹,你有一張?jiān)赗etina顯示屏上的大小為60*61的圖片,而不是60*60的)忠烛。
在前面我們講到属提,要解決問(wèn)題,首先需要找到問(wèn)題在哪美尸。在模擬器上運(yùn)行程序冤议,在“Debug”菜單中選中“Color Misaligned Image”。
這一次有兩種高亮區(qū)域:品紅色區(qū)域會(huì)執(zhí)行子像素渲染师坎,而黃色區(qū)域是圖片大小沒(méi)有對(duì)齊的情況恕酸。
通常,為了解決這個(gè)問(wèn)題胯陋,你只要簡(jiǎn)單地使用ceilf蕊温,floorf和CGRectIntegral方法來(lái)對(duì)坐標(biāo)做四舍五入處理。就是這樣遏乔!
通過(guò)上面的討論义矛,我想建議你以下幾點(diǎn):
對(duì)所有像素相關(guān)的數(shù)據(jù)做四舍五入處理,包括點(diǎn)坐標(biāo)盟萨,UIView的高度和寬度凉翻。
跟蹤你的圖像資源:圖片必須是像素完美的,否則在Retina屏幕上渲染時(shí)鸯旁,它會(huì)做不必要的抗鋸齒處理噪矛。
定期復(fù)查你的代碼量蕊,因?yàn)檫@種情況可能會(huì)經(jīng)常出現(xiàn)。
異步UI
每個(gè)中等以上規(guī)模的應(yīng)用都可能會(huì)使用帶有媒體內(nèi)容的cell:文本艇挨、圖片残炮、動(dòng)畫,甚至還有視頻缩滨。而所有這些都可能帶有裝飾元素:圓角頭像势就、帶‘#’號(hào)的文本、用戶名等脉漏。
我們已經(jīng)多次提及盡可能快地返回cell的需求苞冯,而在這里有一些麻煩:clipsToBounds很慢,圖片需要從網(wǎng)絡(luò)加載侧巨,需要在字符串中定位#號(hào)舅锄,和許多其他的問(wèn)題。
優(yōu)化的目標(biāo)是很明確的:如果在主線程中執(zhí)行這些操作司忱,則會(huì)讓你不能很快地返回cell皇忿。
在后臺(tái)加載圖片,在相同的地方處理圓角坦仍,然后將處理后的圖片制定給UIimageView鳍烁。
立刻顯示文本,但在后臺(tái)定位#號(hào)繁扎,然后使用屬性字符串來(lái)刷新顯示幔荒。
在你的cell中,需要具體情況具體分析梳玫,但主要的思想是在后臺(tái)執(zhí)行大的操作爹梁。這可能不只是網(wǎng)絡(luò)代碼,你需要使用Instruments來(lái)找到他們汽纠。
記孜兰:需要盡快返回cell
有時(shí)候傀履,上面的所有技術(shù)可能都幫不上忙虱朵。如GPU仍然不能使用時(shí)(iPhone4+iOS7),cell中有很多內(nèi)容時(shí)钓账,需要CALayer的支持以實(shí)現(xiàn)動(dòng)畫時(shí)(在drawRect:中實(shí)現(xiàn)起來(lái)真的很麻煩)碴犬。
在這種情況下,我們需要在后臺(tái)渲染所有其他東西梆暮。此外他能在用戶快速滑動(dòng)UITableView時(shí)有效提高FPS服协。
我們Facebook的應(yīng)用。為了檢測(cè)這些啦粹,你可能需要往下滑動(dòng)足夠的高度偿荷,然后點(diǎn)擊狀態(tài)欄窘游。列表會(huì)往上滑動(dòng),因此你可以清楚地看到此時(shí)沒(méi)有渲染cell跳纳,如果想要更精確忍饰,則不能及時(shí)獲得。
這很簡(jiǎn)單寺庄,所以你可以自己試試艾蓝,這時(shí),你需要設(shè)置CALayer的drawsAsynchronously屬性為YES斗塘。
但是我們可以檢查這些行為的必要性赢织,在模擬器上運(yùn)行程序,然后選擇“Debug”菜單中的“Color Offscreen-Rendered”♀擅耍現(xiàn)在所有在后臺(tái)渲染的區(qū)域都被高亮為黃色于置。
如果你為某些層開啟了這一模式,但是它沒(méi)有高亮顯示贞岭,那么它就不夠慢
為了在CALayer層找到瓶頸并進(jìn)一步減少它俱两,你可以使用Instruments里面的Time Profiler。
(關(guān)于Instruments使用工具有Leaks曹步、Allocations宪彩,Time Profiler幫助我們分析代碼的執(zhí)行實(shí)現(xiàn),找出導(dǎo)致程序變慢的原因讲婚,告訴我們‘實(shí)踐都去哪了’)
這里異步優(yōu)化UI的實(shí)現(xiàn)清單:
找到讓你的cell無(wú)法快速返回的瓶頸尿孔。
將操作移到后臺(tái)線程,并在主線程刷新顯示的內(nèi)容筹麸。
最后一招是設(shè)置你的CALayer為異步顯示模式(即使只是簡(jiǎn)單的文本或圖片)這將幫你提高FPS活合。
我嘗試解釋了iOS繪圖系統(tǒng)(沒(méi)有使用OpenGL,因?yàn)樗那闆r更少)的主要思路物赶。
當(dāng)然有些看起來(lái)很模糊白指,但事實(shí)上這只是一些方向,你應(yīng)該朝著這些方向來(lái)檢查你的代碼以找出影響滾動(dòng)性能的所有問(wèn)題酵紫。具體情況具體分期告嘲,但原則是不變的。
獲取完美平滑滾動(dòng)的關(guān)鍵是非常特殊的代碼奖地,他能讓你竭盡iOS的能力讓你的應(yīng)用更加平滑橄唬。完。