影響 UITableView 滾動的流暢性的緣由
一、 在代理方法中作了過多的計算佔用了 UI 線程的時間
二、同上
三、Cell 中 view 的組織複雜
關於第一點,首先要明白 tableview 的代理(這裏指 datasource 和 delegate 的那套方法,下同)方法的調用順序,和時機。對於通常的應用會有以下順序:
一、向代理要 number Of Rows。
二、對於每行向代理要 height For Row At Index Path。
三、向代理要 當前屏幕可見的 cell For Row At Index Path 。(實測顯示4寸屏的手機會取 屏幕顯示數量+2,3.5寸屏同4寸屏數量,雖然3.5寸屏可顯示的cell 數量要小於 4寸屏!)
四、而後 cell 就顯示出來了。
tableView:heightForRowAtIndexPath:
不少人都把優化的重點放到了 cell for row at indexpath 那個方法裏了,在這裏儘量的少計算,可是卻忽略了另外一個很輕鬆就能提高加載時間的方法 :
- - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
Table View 在每次 reload data 時都會要全部 cell 的高度!這就是說你有一百行 cell,就像代理要100次每一個cell 的高度,而不是當前屏幕顯示的cell 的數量的高度!雖然在 iOS 7 下多了計算 cell 高度的方法,可是減小 計算高度時的時間,對於提高加載 Table View 的速度有很是明顯的提升!
- - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
- (**iOS 7專用**)
可是有人說了,我早聽別人說了,reloadData 方法儘可能不要調用,我插入新行都用 insertRowsAtIndexPaths:withRowAnimation: 刪除也用 delete 那個,這個總行了吧?!
這樣也不能忽略 height For Row At Index Path 這個回調的重要性。由於在每次插入或者刪除一行後一樣須要調用一遍 全部行 的這個回調方法!是全部行!你沒看錯,全部只是簡單的減小一個代理方法的計算量,就能夠明顯的提高加載速度。
對於提高 tableView:heightForRowAtIndexPath: 計算量,就是儘量的讓這個方法的計算複雜度爲 O(1),就是隻是簡單的從數組中取一個值,而後返回!
也許有人又要問了,個人應用都是動態的高度,就像微博那樣的,不定數量的文字,可能還有圖片,大小也不固定,這些怎麼返回固定的高度啊?
我指的固定高度不是 row 的高度都同樣那種固定,而是讓在 tableView:heightForRowAtIndexPath: 這個回調裏取這個高度的時間是近乎固定的。
對於高度的計算,還有個小細節須要注意,就是若是 row 的高度都必定,那就刪除代理中的這個 tableView:heightForRowAtIndexPath: 方法,設置 Table View 的 rowHeight 屬性,類似的 numberOfRowsInSection: 系列的方法,我就不都寫出來了。蘋果的文檔裏介紹這樣也能夠減小了調用時間。
如今迴歸正題。對於 cell 高度不固定的,傳統的方法是爲 cell 寫個計算行高的類方法,傳入那些動態的元素(文字,圖片等),而後返回計算後的高度。在 tableView:heightForRowAtIndexPath: 中調用這個方法,填入須要的參數計算cell 高度。這固然沒有什麼問題,只是要是計算量很複雜,你每次 reloadData ,光計算行高就要花去 rowCount * 單行高評價計算時間,想一想有100行,你不按期的須要 reloadData 或者 insert(delete) row......解決辦法就是:
用 「空間換時間」
將計算行高的時間提早到從服務器摟回數據的時候,計算完了高度一併寫回數據庫,別告訴我你在主線程裏阻塞式的處理網絡請求。。。。面壁思過去吧,別浪費了 GCD,NSOperationQueue的青春。最早想到的仍是 NSThread 的同窗,證實你已經老了。。。如今幾乎大部分的多線程操做都不須要用到 NSThread 和 runloop了。
tableView:cellForRowAtIndexPath:
說完了計算 cell 行高的優化,如今來談 tableView:cellForRowAtIndexPath: 回調的優化。優化思路同上,也是經過預處理減小在這個回調中的計算時間。這個回調重點談的是對圖片異步加載的優化。
圖片異步加載無非就是在這個方法裏發起異步請求,圖片加載完後根據 UIImageView 的引用設置圖片。有經驗的程序員可能會使用 懶加載 的方式減小快速滑動時由於網絡請求過於頻繁與切換線程顯示圖片形成的卡頓。這裏還有個問題,拿回來的圖片必定和最後顯示的大小不同,有時候偷懶,直接設置 image view 的 contentMode 屬性要 image view 本身 壓縮。這是一個很取巧的方法,可是對 table view 的滾動速度也會形成 不容忽視 的影響。對圖片變形須要對圖片作 transform ,每次壓縮圖片都要對圖片乘以一個變換矩陣,若是你的圖片不少,這個計算量是不一樣忽視的。
優化建議:從網絡摟回來圖片後先根據須要顯示的圖片大小切成合適大小的圖,每次只顯示處理過大小的圖片,當查看大圖時在顯示大圖。若是服務器能直接返回預處理好的小圖和圖片的大小更好。
使用 Instrument 的 Core Animation 模板能夠查看圖片的壓縮狀況。如圖:
Instrument 中的 Core Animation 模板只有在調試真機時纔有,調試模擬器上的應用沒有這個模板!!!可是能夠在模擬器的 Debug 菜單下找到這些調試選項。
切記:調試應用性能必定要用真機,Mac 的性能完爆 iPhone,全部不要說個人應用在模擬器上調試時不卡啊!模擬器只能模擬 iOS 軟件的運行環境,不能模擬硬件性能!
這些選項對設備的全部應用有效,也就是說你不須要選擇 target 就能調試 它(方便競品分析 :)!
對於 Misaligned images 會有兩種顏色:一種是洋紅色,另外一種是黃色。
洋紅色是由於像素沒對齊,好比上面的 label,通常狀況下由於像素沒對齊,須要抗鋸齒,圖像會出現模糊的現象。
解決辦法:在設置 view 的 frame 時,在高分屏避免出現 21.3,6.7這樣的小數,尤爲是 x,y座標,用 ceil 或 floor 或 round 取整。每 0.5 個點對應一個 pixel,0.3,0.7這樣的就難爲 iPhone 了,低分屏不要出現小數。
黃色是由於顯示的圖片實際大小與顯示大小不一樣,對圖片進行了拉伸,測試顯示使用 image view 顯示實際大小的圖也會變黃。
減小洋紅色和黃色能夠提高滾動的流暢性
手動 Drawing 視圖提高流暢性
若是經過上面的方法,滾動速度還不能達到能夠容忍的速度,那就只剩下最後一個辦法了,手動繪製視圖。
手動繪製方法,不是直接子類化 UITableViewCell,而後覆蓋 drawRect: 方法,這樣你會獲得一個大黑塊!由於 cell 中不是隻有一個 content view。若是不瞭解 cell 的層次結構,能夠用 Reveal 去看下。
繪製 cell 不建議使用 UIView,建議使用 CALayer。 UIView 的繪製是創建在 CoreGraphic 上的,使用的是 CPU。CALayer 使用的是 Core Animation,CPU,GPU 通吃,由系統決定使用哪一個。View的繪製使用的是自下向上的一層一層的繪製,而後渲染。Layer處理的是 Texure,利用 GPU 的 Texture Cache 和獨立的浮點數計算單元加速 紋理 的處理。
GPU 不喜歡 透明,因此全部的繪圖必定要弄成不透明,對於圓角和陰影這些的能夠截個僞透明的小圖而後繪製上去。在layer的回調裏必定也只作繪圖,不作計算!