UITableView无疑是iOS开发中最重要的控件,它的优化直接牵扯到性能和体验,虽然所有优化的基础核心思想都大同小异,但博客鱼龙混杂众说纷纭,各类技巧也是层出不穷看起来都很有道理,于是决定按照自己的理解全面总结一下,便于记忆和重温。
最基础的Cell复用
dequeueReusableCellWithIdentifier,通过Identifier复用。给UITableView添加拓展方法简化cell的复用方法。如下:
- (UITableViewCell*)loadXibCellWithIdentifier:(NSString*)cellIdentifier {
UITableViewCell* cell = [self dequeueReusableCellWithIdentifier:cellIdentifier];
if (nil == cell) {
UINib *nib = [UINib nibWithNibName:cellIdentifier bundle:nil];
[self registerNib:nib forCellReuseIdentifier:cellIdentifier];
cell = [self dequeueReusableCellWithIdentifier:cellIdentifier];
}
return cell;
}
算高
由于算高函数heightForRowAtIndexPath是代理中调用超级频繁的函数,所以,提前算高,并将高度缓存在数据源中是很有必要的。而在iOS8以上提供了设置estimatedRowHeight估算高度后根据cell的自动布局自动高度的方法虽然极度方便(甚至不用实现高度回调函数),但其性能却不容乐观,特别是由于xib的加载是首次加载便全控件加载。
但是如果你的项目已经使用了sb或xib,UITableView+FDTemplateLayoutCell从run loop的角度提供了很优秀的解决方案。
图片缓存
图片大多使用SDWebImage所以图片的内存缓存和硬盘缓存都是默认完成的。
富文本和圆角
两个不可触碰的禁区,代价极其昂贵。
子控件和绘制
subviews自然是越少越好,而且要尽量不透明(即Color Blended Layer选中后的红色区域越少越好)。当遭遇复杂页面的性能瓶颈,用drawRect或其他方法实现控件的全面异步绘制是很好的选择,但这就要求舍弃xib和storyboard,而且布局方面也变得不是十分方便了。
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0); //创建基于位图的上下文
CGContextRefcontext =UIGraphicsGetCurrentContext(); //获取上下文
[[UIColorcolorWithRed:250/255.0green:250/255.0blue:250/255.0alpha:1]set]; //背景色设置
CGContextFillRect(context, rect); //背景色填充
[testString drawInContext:context withPosition:CGPointMake(x, y) andFont:font andTextColor:color andHeight:rect.size.height]; //简单文字绘制
[testImage drawInRect:CGRectMake(x, y, w, h) blendMode:kCGBlendModeNormal alpha:alpha]; //简单图片绘制
最变态的VVeboTableViewDemo
来自于github上一个2k+star的demo,其核心优化思想是:1、异步将内容渲染成图片;2、依照滑动速度,按需加载滚动目标位置前后指定三行的cell。
解读与记录
首先是算高。当获取到数据源之后,应根据数据源和视图布局规则计算出整个cell的frame,用于绘制时的大背景和heightForRowAtIndexPath函数;计算其他可变高度(如textframe)。
然后需要分解cell的布局,基本固定的控件和内容应绘制在一张图片中(如昵称,时间,点赞数等元素),主要使用[string/image drawInRect]的绘制方法(demo中的富文本绘制使用了极度复杂的drawInContext扩展方法,可能是为了兼容iOS8以下)。
而特殊的控件则需要单独处理,如头像(使用白边的中心镂空的蒙版,解决圆角问题),如附图(scrollview),内容(未使用Label,使用以View为基类封装了绘制方法的自定义控件VVeboLabel,具有颜色字体行间距对齐方式等功能,还提供了超链接点击事件(但实现方式过于变态)。
而cell暴露给tableview的属性有数据源字典/模型,接口有draw,clear,releaseMemory(移除时候使用),cellForRowAtIndexPath函数中先调用cell的clear,再赋值data和draw。
最后,依照滚动速度按需加载。scrollViewWillEndDragging委托函数中,根据targetContentOffset和velocity判断需要跳过和需要加载的row,存入needLoadArr用于加载。
网友评论