UITableView+FDTemplateLayoutCell源碼學習筆記

UITableView-FDTemplateLayoutCell是一個自動計算cell高度並緩存從而達到順滑滾動tableview的效果。github地址戳這裏git

基本原理是經過緩存每一個cell的高度,當tableview回調delegate的heightForRowAtIndexPath的時候,省去了計算cell高度計算,極大的提升了tableview的滑動體驗。github

由於 heightForRowAtIndexPath這個接口會在reloadData的時候,每一個cell都會調用一次,同時滾動tableview的時候,cell從不可見到可見區域也會被調用一次heightForRowAtIndexPath,因此這個API的調用是很頻繁的。因爲每一個cell的高度是動態的,因此每次要作高度計算,都要從新layout一遍,而後得出高度,這塊計算量是比較大的。sunny經過將每一個cell的計算好的高度緩存起來,下次獲取相同位置cell的高度的時候,直接返回緩存的高度。
緩存

同時,當第一次reloadData,或者cell的行數發生變化(增減行,section) ,會先在tableview不處於滾動狀態的時候異步計算那些沒有被計算過的cell的高度,作預緩存,這塊很是贊。就是使用者須要當心,因爲這塊是異步的, tableview delegate有可能會在預緩存計算的時候不存在了,致使程序崩潰,因此使用者在tableview須要析構的時候,在對應的tableview controller的dealloc中講self.tableview.delegate = nil;,確保delegate後續不會是一個髒對象。異步

 

fd_heightForCellWithIdentifier: cacheByIndexPath: configuration: ide

  1. 若是沒有打開預緩存開關,則打開該開關,而後異步作一遍預緩存行高計算
  2. 若是緩存了改行的高度則返回行高
  3. 若是麼有緩存,則計算行高fd_heightForCellWithIdentifier: configuration,緩存行高並返回

 

fd_heightForCellWithIdentifier: configurationoop

  1. 首先獲取針對這個identifier類型的模板cell,sunny爲每個類型cell都緩存了一個模板cell,這塊我還不明白爲何要這麼作,爲何不直接用系統dequeue的重用cell來作?
  2. 將須要計算高度的cell數據填充到這個template cell,而後就能夠計算cell高度了
  3. 計算tableview.contentView的真實寬度。
    1. 若是有定製accessoryView,則去除這個寬度
    2. 若是有系統accessoryView展現,則去除對應的寬度。
  4. 檢查是不是否是自動佈局,判斷是否自動佈局的標準是contentView自己至少存在一個約束。因此咱們在CustomCell作約束的時候,須要至少指定contentView的一個約束。
  5. 若是是自動佈局,則將contentView的寬度約束添加進去,這樣作的目的是讓UILabel之類的內容可能多行的控件適應這個寬度折行(固然前提是咱們已經設置好了這些控件的佈局約束)。而後調用systemLayoutSizeFittingSize來計算高度。最後移除剛纔臨時添加的contentView寬度約束。
  6. 若是是絕對佈局,則custom cell要重寫sizeThatFits接口本身提供行高計算。若是沒有重寫,則會報錯。
  7. 若是存在分割線,則添加1px的高度。由於CGSize是基於point,因此高度計算是 1/[UISscreen mainScreen].scale
  8. 返回計算好的高度。

 fd_templateCellForReuseIdentifier佈局

  1. 緩存字典是否存在,沒有則建立一個。
  2. 是否緩存了identifier指定類型的cell,若是沒有dequeueReusableCellWithIdentifier一個。
  3. 設置celltranslatesAutoresizingMaskIntoConstraints=NO,只支持自動佈局和絕對佈局兩種。
  4. 將cell緩存並返回。

fd_precacheIfNeededui

 這個方法會在第一次計算行高,或者tableview的數據發生改變的時候(reloadData,增減行,sections)被異步調用。spa

這塊會對全部須要預先計算的cell都計算一次行高對象

  • 爲了不計算影響到tableview的順暢滑動,計算只發生在runLoop處於default mode
  • 只在ui main thread徹底處於空閒狀態準備進入waiting狀態以前才進行計算。當開始滾動的時候,runloop處於UITrackingRunLoopMode, 這塊計算不會被執行。
  • 爲了讓runloop的一次執行時間縮減到最小,每次只向runloop提交一個cell的高度計算任務,執行完才產生下一個。

很喜歡sunny的這塊處理,小而精巧。有關RunLoop的深刻介紹,參見這篇強文-深刻理解RunLoop

 FDTemplateLayoutCellAutomaticallyCacheInvalidation

這個category的目的是利用swizzle機制重寫了涉及數據變化的delegate方法,例如reloadData, nsertRowsAtIndexPaths等。當回調delegate的這些方法的時候,其實是回調對應的fd_開頭的同名方法,在這些方法裏面作對應的緩存高度操做,或者預緩存新產生的cell高度,或者講刪除cell的高度從緩存中刪除掉。

相關文章
相關標籤/搜索