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是否提供了这个机制。

c++类里面的NSObject成员会在类析构时自动dealloc掉,适用于STL容器。

5·UIScrollView的layoutSubviews调用时机

UIScrollView先进行setContentSize再进行addSubview操作,会执行layoutSubviews多次,而如果把setContentSize操作放到最后,那么只会执行一次layoutSubviews

6·UITableView的cell重用限制

UITableView并未对重用cell的数量做限制,在测试过程中,被划出屏幕外的所有cell都没被销毁。这个测试过程中,将cell按顺序用height递增,即每个cell的高度越来越大,那么将会导致一个情况,越是滑动到后面,显示区域里面的cell会越少(相比之前滑动经过的区域来说),导致了进入重用的cell数量递增(因为前面一屏显示的cell数量会更多),那么这些等待重用的cell虽然数量很大也不会被释放掉的

7·实用的hidden。

UITableView对滑出屏幕的cell都是进行hidden的而非remove,在设计cell重用缓存的时候,发现一旦频繁的进行cell的remove操作会比使用hidden的cpu使用率高上10%(在iPhone5上面),而UITableView好像早前版本是remove现在也改为hidden了。

在实际开发中,如果不是得销毁UIView,那么hidden是比直接remove来得快。另外设置hidden为YES会触发一次其子视图的removeFromSuperView操作。

8·xib和纯frame设置的问题

xib加载的视图再用frame进行设置,接着如果改变父视图的frame会导致其height变为0,那么就是因为xib加载的这个视图的默认autoresizingMask有height相关的,置为none即可。这个问题之前很困扰,一直没有发现这个猫腻,当然,只有在前面提到的这个特定条件下(手动改变视图frame,而其上面的子视图又是通过xib加载的)才会出现的。

9·cell相关。

(1)dequeueReusableCellWithIdentifier:identifier这个函数不调用并不能帮助你实现cell的不重用,仅需要在进行初始化的时候把reuseIdentifier赋值为nil即可,在这里initWithFrame创建的cell应该就是默认reuseIdentifier为nil了,因为UITableViewCell的NS_DESIGNATED_INITIALIZER不在initWithFrame。

(2)UITableViewCell被点选的触发操作,其实并非在cell内部实现手势点击,而是通过在UITableView里面通过获得当前点击的位置,来判断点击的是哪一个cell,接着调用cell的setSelected:animated函数的。这样做应该也是为了避免耦合吧。

(3)UITableViewCell的选中高亮。当选中cell的时候,你会发现cell上面的所有视图,都变成了clearColor来让cell的高亮背景色显示出来,而当取消cell高亮的时候又会变回去,那么这个问题出现了。这些子视图的背景色被动了!那么原本的背景色又被记录在哪里呢,需要复原的时候又能跑出来。

在这里有2个思路:

·继承UIColor来写一个类,提供一个属性,受记录的颜色,当setBackgroundColor的时候碰到的是这个class,就用他记录的颜色来设置颜色。每次要将所有子视图都设置为clearColor的时候,就将这些子视图的背景色都用这个类记录下来。

·用一个容器存储这些视图对应的背景色,按键值对应,这里的key则选用每个UIView的内存地址。

·UITableView使用了一个UIView的私有api来解决这个问题:-[UIView _descendent:willMoveFromSuperview:toSuperview:]。

很重要的一点是,当cell上面的willRemoveSubview被触发时,如果当前cell呈被选中高亮状态,他就会帮这个要被remove的子视图回复原有的背景色。

(4)UITableViewCell的layoutSubviews。一般来讲cell上面的那些默认元素,titleLabel和删除按钮左右滑动的,很多时候是没有用的。而一旦在UITableViewCell子类的layoutSubviews里面有super layoutSubviews,也会照顾到这些默认视图,实测中也会使cpu的占用率下滑几个%。


MPTableView对UITableView的改进:

·提供了方法- (void)reloadDataAsyncWithCompletion:(void(^)(void))completion来进行异步的加载cell高度,选择了gcd的全局队列去计算和缓存高度完了再主线程刷新tableview——在这个过程中,如果tableview先前还有内容显示,那么在这个异步计算过程中,仍然可以滑动浏览原本的内容。

·修复了update动画,特别针对UITableView的plain模式下,进行多路的insert/delete/reload操作之后,section的header/footer都会有很奇怪的运动轨迹(这个情况也发生在Mac OX下的NSTable组件)。同时提供了insert/delete等的自定义动画方法。

·可以选择强制在reload的时候,继续重用之前产生的cell重用对象。UITableView的每次reload都会清除所有数据,可是很多时候,其实cell都是跟先前的一样,这样每次reload,无疑是多了删除旧的cell和创建新的过程——而实际上可以使用旧的。

·多路的update操作支持,可以同时/延迟开启多个update事务,并且设定不同时间、动画状态。

·同样的子视图样式下,cpu的占用率一定会比UITableView来得低。在同样的update操作下面,cpu的占用率也更低。还有cell拖动模式下也是。

·以及其他的一些改进和新增api,可以在更低系统版本下使用新的api。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容