UITableView图文混排自动布局滑动优化实战
AutoLayout和手动计算高度
毫无疑问,使用AutoLayout会明显的比手动计算高度慢,那么我为什么要用AutoLayout呢,因为实在太方便了,而且视图太复杂,产品改的太频繁,手动计算实在工作量太大,维护起来超级麻烦。
而且新的技术出来了,不用不是亏了吗。
方案
1.缓存高度
既然手动计算高度更快,那就在Reuse
的时候用AutoLayout帮我们算过后的高度就行了,缓存一个高度字典(或者数组),在算完渲染出来的时候取高度,在取高度的时候做个判断就行。
//保存高度
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
[_cellHeightsDic setObject:@(cell.height) forKey:indexPath];
}
//设置高度
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSNumber *height = [_cellHeightsDic objectForKey:indexPath];
if (height) return height.doubleValue;
return 400;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSNumber *height = [_cellHeightsDic objectForKey:indexPath];
if (height) return height.doubleValue;
return UITableViewAutomaticDimension;
}
2.图片和内容懒渲染
看不见的东西就不要让他渲染出来,这一步的优化是基于cellForRowAtIndexPath
函数比willDisplayCell
会先调用,如果在构造cell
的时候就把所有内容填充上去,是一种浪费。因此可以把很重的内容,比如图片放到willDisplayCell
的时候再加载。
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
[_cellHeightsDic setObject:@(cell.height) forKey:indexPath];
[cell willDisplay];
}
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if ([tableView.indexPathsForVisibleRows indexOfObject:indexPath] == NSNotFound)
{
[(ABMainPageBaseTableViewCell*)cell endDisplay];
}
}
然后在willDisplay
和endDisplay
里做些特殊的处理,对于UIImageView
就可以
-(void)endDisplay{
[imageView setImage:nil];
[imageView stopAnimating];
}
3.预加载
预加载分为两种,一种是图片预加载,另一种是内容预加载,先说内容预加载,其实就是在指定滚动到第几个cell
的时候开始分页请求,这样用户就会无感知的开开心心的刷刷刷了。
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
[_cellHeightsDic setObject:@(cell.height) forKey:indexPath];
[cell willDisplay];
if(_dataSource.count - indexPath.row < 6){
//剩下五个内容就立马开始刷新
[self loadMore];
}
}
要注意的是控制好你的网络请求,保证一次只发起一次loadMore
请求,不要重复加载了。
图片预加载,可以直接使用SDWebImagePrefetcher
,下载图片
[[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:imgUrls];
会自动创建网络请求下载图片,下载完存入内存和本地缓存里,下次使用直接使用sd_setImageWithUrl
会自动去内存里寻找下载完的图片。
4.GIF特殊处理
如果GIF太多了,做完以上优化,会发现滑动到GIF的时候还是很卡,原来是因为SDWebImage
直接把下载完的GIF内容直接填充给UIImageView
,会直接按帧把动画渲染出来,边滑动边渲染图片到UIImageView
上,就会导致UI线程阻塞,用户就感觉到卡顿了。
于是尝试手动解GIF数据,使用第三方库FLAnimatedImage手动解GIF,在渲染的时候从内存读入缓存完的NSData
,庆幸的是最新的SDWebImage
已经支持了FLAnimatedImage
,因此可以直接愉快的sd_setImage
了。
最后要做的就是把滑动和GIF动画分开,想到的是NSRunLoop
,因为滑动事件是在NSEventTrackingRunLoopMode
下的,使用NSDefaultRunLoopMode
就可以保证不在UI动画的时候取帧和渲染GIF。
直接设置FLAnimatedImageView
的runLoopMode
即可。