什么样的tableView是很优异的?
- 滚动时,确保cell刷新帧率应该在 50+/s !
- 展示cell时,不能出现跳帧现象,导致换面不流畅。
- 最关键的是在创建cell或者从缓存池取cell时,让系统花费最少的时间,即尽可能的减少显示cell的计算量。
1. 行高要缓存
举个简单的栗子:如果现在要显示100个Cell,当前屏幕显示5个。那么全局刷新UITableView时, UITableView会先调用100次tableView:heightForRowAtIndexPath:
方法,然后调用5次 tableView:cellForRowAtIndexPath:
方法;滚动屏幕时,每当Cell滚入屏幕,都会调用 一次tableView:heightForRowAtIndexPath:
和tableView:cellForRowAtIndexPath:
方 法。所以说要提前计算并缓存好高度,因为heightForRowAtIndexPath:
是调用最频繁的方法,如果是使用MVVM搭建框架,可以在viewModel中添加行高属性,提前计算好行高。
如果是固定高度的cell,可以直接采用tableView.rowHeight = 88;
的方式,这样写,就需要实现tableView:heightForRowAtIndexPath:
方法了;如果实现这个方法,设置rowHeight
就会失效。
2.不要动态创建子视图
意思是:cell所有的子视图都预先在初始化方法中创建,如果根据实际情况不需要显示的可以设置 hidden.这样能尽可能的减少cell创建或从缓存池取时因为布局子控件所消耗的时间.
3. 渲染
为了保证TableView的流畅,当快速滑动的时候,cell必须被快速的渲染出来。所以cell渲染的速度必须快。如何提高cell的渲染速度呢?
(1)当有图像时,预渲染图像,在bitmap context先将其画一遍,导出成UIImage对象,然后再绘制到屏幕,这会大大提高渲染速度。具体内容可以自行查找“利用预渲染加速显示iOS图像”相关资料。
(2)渲染最好时的操作之一就是混合(blending)了,所以我们不要使用透明背景,将cell的opaque
值设为Yes,背景色不要使用clearColor,尽量不要使用阴影渐变等
(3)由于混合操作是使用GPU来执行,我们可以用CPU来渲染,这样混合操作就不再执行。可以在UIView的drawRect方法中自定义绘制。
4.减少视图的数目
当cell上子视图过多或者层级关系过于复杂的时候,cell的布局计算会大大增长,可能会导致一个runloop流程渲染不及时,导致卡顿。
5.减少多余的绘制操作
在实现drawRect方法的时候,它的参数rect就是我们需要绘制的区域,在rect范围之外的区域我们不需要进行绘制,否则会消耗相当大的资源。
6.异步化UI,不要阻塞主线程
使用Texture。可以保持复杂用户界面的流畅和响应。Texture的node是线程安全的,通过将图像解码、文本绘制和渲染等操作从主线程中迁移,从而保证主线程的响应。
7. 不要给cell动态添加subView
在初始化cell的时候就将所有需要展示的添加完毕,然后根据需要来设置hidden
属性显示和隐藏。
8. 懒加载图片
快速滑动的时候,不要加载图片,滑动停止时,再加载图片。这样做可以防止在快速滑动时加载图片导致的卡顿问题。
9. 圆角优化
圆角的不合理使用,会导致离屏渲染,触发缓冲区的频繁合并和上下文的的切换。所以需要合理的设置视图的圆角,避免离屏渲染。
使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"myImg"];
// 开始对imageView进行画图
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
// 使用贝塞尔曲线画出一个圆形图
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
// 结束画图
UIGraphicsEndImageContext();
[self.view addSubview:imageView];