影响 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的回调里必定也只作绘图,不作计算!