UITableView优化的那些事儿

做为iOS开发,UITableView多是平时咱们打交道最多的UI控件之一,其重要性不言而喻。ios

关于TableView,我想最核心的就是UITableViewCell的重用机制了。git

简单来讲呢就是当TableView滚动时,会调tableView:cellForRowAtIndexPath:这个方法,TableView只会建立屏幕内或者只比屏幕多一点点的cell,当滚动须要展示新的cell的时候,TableView首先会把已经移出屏幕外的cell放入到缓存池中去,而后再从缓存池中取出新的cell用来展现,当缓存池中没有的时候,则会建立新的cell。可是cell可能不只仅是一种,咱们怎么来辨别咱们须要的cell呢?苹果公司已经为咱们作好了一切,咱们只须要简单地设置一个identifier便可,TableView即可自动根据identifier从缓存池中去出相应cell出来复用。这样就极大的节省了内存的开销。 github

知道cell的复用原理后,咱们再来看看TableView的回调方法。咱们知道,TableView继承自UIScrollView,必须先肯定它的contentSize和每一个cell的位置,这样才能正确的放置每一个cell。因此在建立或者复用cell以前,tableView会调用tableView:heightForRowAtIndexPath:来肯定contentSize和每一个cell的高度,以后再调用tableView:cellForRowAtIndexPath:显示相应的cell。然而此举对于那些成百上千不定高的cell,计算高度会至关消耗性能。缓存

因此首先咱们围绕cell来看看TableView如何进行优化。多线程

1.cell复用异步

这个很简单,只要注册一下,便会自动复用ide

    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *Identifier = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (!cell) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
        }
        
        return cell;
    }

这里说一句,有不少人会在这里给cell进行赋值操做,绑定数据,可是最近看了一篇文章https://medium.com/ios-os-x-development/perfect-smooth-scrolling-in-uitableviews-fd609d5275a5#.373u9fh4p,里面说到不要在这个方法中进行数据绑定,由于TableView会为每一个cell调用一次这个方法,它应该快速执行,咱们应该快速的返回cell重用实例。咱们能够在tableView:willDisplayCell:forRowAtIndexPath:这个方法中进行数据绑定。性能

2.cell的高度计算优化

这边咱们分为两种cell,一种是定高的cell,另一种是动态高度的cellui

a.定高的cell,应该采用以下方式:

self.tableView.rowHeight = 88;

这个方法指定了全部cell高度都是88的tableview,rowHeight默认的值是44,因此一个空的TableView会显示成这个样子。对于定高cell,直接采用上面方式给定高度,不须要实现tableView:heightForRowAtIndexPath:以节省没必要要的计算和开销。

b.动态高度的cell

咱们须要实现它的代理,来给出高度:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // return xxx
}

这个方法给出后,上面的rowHeight的设置将会变成无效。在这个方法中,咱们须要提升cell高度的计算效率,来节省时间。

须要说明的是自从iOS8以后有了self-sizing cell的概念,cell能够本身算出高度,但目前市面上的公司最低支持iOS8,能用上这个方法可能还有很久。

除了提升cell高度的计算效率以外,对于已经计算出的高度,咱们须要进行缓存,对于已经计算过的高度,没有必要进行计算第二次。

此外,具体对于如何优化cell高度计算,什么时候缓存cell高度,这篇博客给出了很是好的说明,强烈推荐有兴趣的深读一下。http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/

3.渲染 

为了保证TableView的流畅,当快速滑动的时候,cell必须被快速的渲染出来。因此cell渲染的速度必须快。如何提升cell的渲染速度呢?

a.当有图像时,预渲染图像,在bitmap context先将其画一遍,导出成UIImage对象,而后再绘制到屏幕,这会大大提升渲染速度。具体作法能够参考:《利用预渲染加速显示iOS图像》

b.渲染最好时的操做之一就是混合(blending)了,因此咱们不要使用透明背景,将cell的opaque值设为Yes,背景色不要使用clearColor,尽可能不要使用阴影渐变等

c.因为混合操做是使用GPU来执行,咱们能够用CPU来渲染,这样混合操做就再也不执行。能够在UIView的drawRect方法中自定义绘制,具体可参考:http://southpeak.github.io/blog/2015/12/20/perfect-smooth-scrolling-in-uitableviews/

4.减小视图的数目

咱们在cell上添加系统控件的时候,实际上系统都会调用底层的接口进行绘制,大量添加控件时,会消耗很大的资源而且也会影响渲染的性能。当使用默认的UITableViewCell而且在它的ContentView上面添加控件时会至关消耗性能。因此目前最佳的方法仍是继承UITableViewCell,并重写drawRect方法。

5.减小多余的绘制工做

在实现drawRect方法的时候,它的参数rect就是咱们须要绘制的区域,在rect范围以外的区域咱们不须要进行绘制,不然会消耗至关大的资源

6.不要给cell动态添加subView

在初始化cell的时候就添加好,而后根据须要来设置hide属性显示和隐藏

7.异步化UI,不要阻塞主线程

咱们时常会看到这样一个现象,就是加载时整个页面卡住不动,怎么点都没用,仿佛死机了通常。缘由是主线程被阻塞了。因此对于网路数据的请求或者图片的加载,咱们能够开启多线程,异步话操做

8.滑动时按需加载对应的内容

//按需加载 - 若是目标行与当前行相差超过指定行数,只在目标滚动范围的先后指定3行加载。
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
    NSIndexPath *ip = [self indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];
    NSIndexPath *cip = [[self indexPathsForVisibleRows] firstObject];
    NSInteger skipCount = 8;
    if (labs(cip.row-ip.row)>skipCount) {
        NSArray *temp = [self indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, self.width, self.height)];
        NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];
        if (velocity.y<0) {            
 NSIndexPath *indexPath = [temp lastObject];           
  if (indexPath.row+33) {                 
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]];
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]];                 [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]];             }         }         [needLoadArr addObjectsFromArray:arr];     } }

记得在tableView:cellForRowAtIndexPath:方法中加入判断:

if (needLoadArr.count>0&&[needLoadArr indexOfObject:indexPath]==NSNotFound) {
    [cell clear];
    return;
}

滑动很快时,只加载目标范围内的cell,这样按需加载(配合SDWebImage),极大提升流畅度。

最后,对于TableView的优化还有不少方面没有说起,但愿你们多多交流~

参考文章

https://medium.com/ios-os-x-development/perfect-smooth-scrolling-in-uitableviews-fd609d5275a5#.373u9fh4p

http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/ 

http://southpeak.github.io/blog/2015/12/20/perfect-smooth-scrolling-in-uitableviews/

相关文章
相关标签/搜索