UITableView的原理——探究及重新实现代码
转自简书,原文地址,本文主要探讨一些特殊细节,像视图重用这类最基本的原理可在源码里查看。
先前重新实现了一个list容器视图,由于Apple没有开源,在此分享过程中探索到的UITableView一些细节。MPTableView: A list view like UITableView, more fast, more features.
1·捉摸不定的contentOffset
UISrollview在滑动的时候,要获取其不断变化的contentOffset值,可通过其协议来获取也可以在其layoutSubviews里面获得,而后者所获取到的offset值会来得频繁很多——当快速滑动的时候,scrollView的协议回调次数远远低于layoutSubviews调用次数,也即contentOffset的获取次数更少,那样一旦需要根据contentOffset来做某些精确的工作的话,则效果会更差。
即使选择最容易被调用的layoutSubviews,其调用次数也并非都是线性变化的,layoutSubviews被回调的次数是有限(n次1s)的,所以一旦急速滑动,并不会逐像素回调,而是总的滑动距离内调用一定次数,最后每次获得的contentOffset也将呈跳跃状。
2·关于UIView的视图层次
发现在addSubview之后通过insertSubview: AtIndex:来设置子视图的层级后,一旦重新修改子视图的frame则这个index将失效。而后面发现了通过view.layer.zPosition的提升则可以令view在其父视图中一直处于高层级或者低层级。
这个是在实现plain模式下tableview的section header/footer停浮时候,需要解决的问题。因为section视图很多是比cell早加入显示区域的(header),那么当滚动到需要header浮在cell的头上时(遮住),则会出现cell遮住header的情况。一开始选择了zPosition,但是发现这个就破坏了视图的原始状态,后面直接bringToFront实现plain的section悬浮。
3·update相关
多路insert/delete/reload操作。
(1)最好把某种操作的IndexSet/Array全部整合成一个数组,这样最多就只有3个数组了
(2)这3种操作中reload是优先进行的,至于insert和delete,UIKit是让delete先进行,再进行insert,在这里面,reload其实就是做了先delete后insert操作。
(3)经常可以看见某些app进行reload某个cell来进行扩张其内容(cell的height变大)的效果,如果这个cell底部有分割线或者是其他内容的话,那么在这个扩张cell的过程中,动画效果是这个cell下面的cell往下移动,cell的高度被扩充,但是那个分割线却没有移动的效果而是直接出现在了最底部。这是由于我们通常选择了None的动画方式来进行reload,而None的动画方式其实就是单纯的视图hidden.
(4)willInsertCell这个协议在insert动画之前是会被回调的,如果在insert操作里面选择了None的动画枚举,那么兴许可以通过这个协议做点自定义效果动画。这个不知道apple是否提供了这个机制。
4·UIScrollView的layoutSubviews调用时机
UIScrollView先进行setContentSize再进行addSubview操作,会执行layoutSubviews多次,而如果把setContentSize操作放到最后,那么只会执行一次layoutSubviews
5·UITableView的cell重用限制
UITableView并未对重用cell的数量做限制,在测试过程中,被划出屏幕外的所有cell都没被销毁。这个测试过程中,将cell按顺序用height递增,即每个cell的高度越来越大,那么将会导致一个情况,越是滑动到后面,显示区域里面的cell会越少(相比之前滑动经过的区域来说),导致了进入重用的cell数量递增(因为前面一屏显示的cell数量会更多),那么这些等待重用的cell虽然数量很大也不会被释放掉的。
6·实用的hidden。
UITableView对滑出