美文网首页Swiftios开发
2019-03-31 UITableView的行高缓存优化以及原

2019-03-31 UITableView的行高缓存优化以及原

作者: Daniel梁 | 来源:发表于2019-03-31 18:36 被阅读0次

    我们先抛出一个问题

    当我们cell的行高随网络的数据不同出现变化的时候,我们要怎么做去保证用户滚动的时候不会感觉到卡顿呢。

    如下图,我们有不同的图片在不同的View中。

    屏幕快照 2019-03-31 下午4.00.51.png

    我们可以通过TableView 中 DataSource的代理方法

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    

    每次model改变的时候,我们通过调用tableView的reloadData,该方法的底层会清除所有的cell ! 并重新计算section高度。

    由于他会清理所有的cell再去创建,所以我们传数据的时候最好一次把单个模型封装成一个模型数组,一次把全部数据传值,这样就不会调用太多次reloadData

    reloadData的源码

    - (void)_reloadDataIfNeeded
    {
        if (_needsReload) {
            [self reloadData];
        }
    }
     
    - (void)reloadData
    {
        //当数据源更新后,需要将所有显示的UITableViewCell和未显示可复用的UITableViewCell全部从父视图移除,
        //重新创建
        [[_cachedCells allValues] makeObjectsPerformSelector:@selector(removeFromSuperview)];
        [_reusableCells makeObjectsPerformSelector:@selector(removeFromSuperview)];
        [_reusableCells removeAllObjects];
        [_cachedCells removeAllObjects];
     
        _selectedRow = nil;
        _highlightedRow = nil;
     
        // 重新计算 section 相关的高度值,并缓存起来
        [self _updateSectionsCache];
        [self _setContentSize];
     
        _needsReload = NO;
    }
    

    更多UITableView源码 http://www.cocoachina.com/ios/20161129/18220.html

    问题又来了,我们调用了reloadData后,那么rowHeight的代理方法会怎么调用呢

    IOS7

    • 在ios7及以前,我们可以通过设置tableView的rowHeight属性或者实现dataSource的rowHeight代理方法去设置行高,苹果建议两者设置其中一者即可,想一下也是,两个都设置了其中一个会失效。

    IOS8

    • 在ios8以后,tableView中增加了新的属性tableView.estimatedRowHeight,及预估高度。苹果的初衷是他每个cell的高度会随数据源变化而变化,增加一个预估高度就可以预估出需要的高度,用户滚到下一个cell的时候再去计算其高度

    可以看到ios8以后,苹果是推荐我们用预估行高去优化的,不然他也不会推出该属性

    下面我们来看一下ios7和ios8的在页面滚动的时候调用rowHeight代理方法的次数


    屏幕快照 2019-03-31 下午6.20.32.png

    这图是来http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/
    TemplateLayoutCell的开发团队,文章讲的不错可以去看看

    我们抛开ios7不说,我们看看ios8后的事情,可以看到开启估算后计算的次数比关闭估算少很多次,关闭估算不仅在一开始就计算好所有cell的高度,而且在滚动的时候又重新去计算一次。。。开启估算滚动的时候会去计算高度

    测试

    下面来看看我在项目里面测试的数据
    首先我cell的数据数量是16个左右,我现在要测试开关estimate调用了几次rowHeight

    打开估算estimatedRowHeight = 400

    屏幕快照 2019-03-31 下午6.28.23.png

    可以看到界面有三个cell ,每个cell调用了三次计算高度,一共九次
    继续滑动的时候会再去计算其他cell的rowHeight,也是一个cell调用三次rowHeight。

    关闭估算

    屏幕快照 2019-03-31 下午4.12.42.png

    把16个数据的高度算出来了,而且滚动的时候会继续去计算,性能很糟糕.

    到这里,我们可以想出对应的优化思路了,既然他每个cell都会去重新计算,课室cell的model是不变的,16个数据,我们缓存他16个高度就可以了,这样就不会重复去计算高度了。

    模型中增加缓存高度的属性

     //缓存行高
        lazy var rowHeight : CGFloat = {
        print("计算了rowHeight")
            //类对象 引用类型
            let cell = StatusCell.init(style: .default, reuseIdentifier: StatusCellID)
            return cell.RowHeight(statusVM: self)
        }()
    

    rowHeight调用缓存高度

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
          
            return statuslistviewModel.StatusList[indexPath.row].rowHeight
        }
    

    计算rowHeight的方法调用了16次,往回滚的时候该方法没有被调用
    说明使用了缓存,没有重新去计算


    屏幕快照 2019-03-31 下午6.35.02.png

    相关文章

      网友评论

        本文标题:2019-03-31 UITableView的行高缓存优化以及原

        本文链接:https://www.haomeiwen.com/subject/pzrjbqtx.html