Swiftios开发

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

2019-03-31  本文已影响0人  Daniel梁

我们先抛出一个问题

当我们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

IOS8

可以看到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
上一篇下一篇

猜你喜欢

热点阅读