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的高度从缓存中删除掉。

相关文章
相关标签/搜索