关于iOS UITableView estimatedRowHeight使用总结

UITableView从iOS7.0以后增加了estimatedRowHeight 属性及对应的tableView:estimatedHeightForRowAtIndexPath:代理方法。苹果官方文档是这样描述estimatedRowHeight属性的:

摘要.png

文档描述的大概意思是,提供一个非负值预估高度可以提高加载表视图的性能,将一些几何计算的成本从加载时间推迟到滚动时间,当创建self-sizing table view cell的时候,需要设置此属性并使用约束来定义cell size。estimatedRowHeight属性的默认值为UITableViewAutomaticDimension,设置为0时表示禁用此属性。这里要注意一点,estimatedRowHeight在iOS11之前默认值为0,在iOS11之后,默认值为非0值。

上面文档简要说明了为UITableViewCell设置预估高度的一些作用,但它具体有哪些直接的表现呢?Talk is cheap.Show me the code.接下来,我们通过代码来说明一下。

利用KVO对tableView conentSize变化监听
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"contentSize"]) {
         NSLog(@"此时TabbleView contentSize 值== %@",  NSStringFromCGSize(self.tableView.contentSize));
    }
}
打印tableView代理方法名
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    NSLog(@"%s",__FUNCTION__);
    return 5;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *cellId = @"CellID";
    UITableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:cellId];
    if (!cell) {
        tableViewCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellId];
        tableViewCell.backgroundColor = [UIColor redColor];
    }
    NSLog(@"%s",__FUNCTION__);
    return tableViewCell;
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"%s",__FUNCTION__);
    return 270;
}
//- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
//    NSLog(@"%s",__FUNCTION__);
//    return 200;
//}

现在,我们为上述的tableViewCell设置不同的预估高度,来看一下代理方法中 NSLog的打印情况。

1.estimatedRowHeight设置为0

由于不同的系统版本estimatedRowHeight默认值不同,所以这里直接设置estimatedRowHeight为0,表示禁用此属性。当然,也可以在对应的代理方法进行设置。

-[ViewController tableView:numberOfRowsInSection:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
 此时TabbleView contentSize == {414, 1350}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]

从上面打印可以看出,在禁用estimatedRowHeight情况下,tableView:heightForRowAtIndexPath:代理方法先执行了5次,然后才执行了tableView:cellForRowAtIndexPath:代理方法,此时tableView contentSize为{414, 1350}。当我们滚动tableView的时候,tableView contentSize没有变化。

2.tableViewCell 预估高度小于实际高度

我们把上面打印tableView代理方法名中设置预估高度的tableView:estimatedHeightForRowAtIndexPath:代理方法注释去掉,这时每个tableViewCell的预估值高度为200。其实,这里直接为estimatedRowHeight属性赋值也是一样的,但是为了更全面地演示代码的执行过程,所以直接用代理方法设置tableViewCell 预估高度。

-[ViewController tableView:numberOfRowsInSection:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1000}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1070}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1140}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1210}

从上面打印可以看出,tableView:estimatedHeightForRowAtIndexPath:代理方法先执行了5次,然后才执行了tableView:cellForRowAtIndexPath:和tableView:heightForRowAtIndexPath:代理方法。此时从下往上滚动tableView,tableView contentSize的高度以70的差值的增加。

3.tableViewCell 预估高度大于实际高度

我们把上面打印tableView代理方法名中设置预估高度的tableView:estimatedHeightForRowAtIndexPath:代理方法的返回值设置为300,大于实际高度270的值。

-[ViewController tableView:numberOfRowsInSection:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1500}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1470}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1440}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1410}

从上面打印可以看出,tableView:estimatedHeightForRowAtIndexPath:代理方法先执行了5次,然后才执行了tableView:cellForRowAtIndexPath和tableView:heightForRowAtIndexPath:代理方法。此时,从下往上滚动tableView,tableView contentSize的高度以30(即:预估高度-实际高度)的差值减少。

4.tableViewCell 预估高度等于实际高度

我们把上面打印tableView代理方法名中设置预估高度的tableView:estimatedHeightForRowAtIndexPath:代理方法的返回值设置为270,等于实际高度270的值。

-[ViewController tableView:numberOfRowsInSection:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
-[ViewController tableView:estimatedHeightForRowAtIndexPath:]
 此时TableView contentSize == {414, 1350}
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:cellForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]
-[ViewController tableView:heightForRowAtIndexPath:]

从上面打印可以看出,tableView:estimatedHeightForRowAtIndexPath:代理方法先执行了5次,然后才执行了tableView:cellForRowAtIndexPath和tableView:heightForRowAtIndexPath:代理方法,此时从下往上滚动tableView,tableView contentSize没有变化。

总结

1.在禁用tableViewCell预估高度的情况下,系统会先把所有tableViewCell实际高度先计算出来,也就是先执行tableView:heightForRowAtIndexPath:代理方法,接着用获取的tableViewCell实际高度总和来参与计算tableView contentSize,然后才显示tableViewCell的内容。在这个过程中,如果实际高度计算比较复杂的话,可能会消耗更多的性能。

2.在使用tableViewCell预估高度的情况下,系统会先执行所有tableViewCell的预估高度,也就是先执行tableView:estimatedHeightForRowAtIndexPath:代理方法,接着用所有tableViewCell预估高度总和来参与计算tableView contentSize,然后才显示tableViewCell的内容。这时候从下往上滚动tableView,当有新的tableViewCell出现的时候,如果tableViewCell预估值高度减去实际高度的差值不等于0,tableView contentSize的高度会以这个差值来动态变化,如果差值等于0,tableView contentSize的高度不再变化。在这个过程中,由之前的所有tableViewCell实际高度一次性先计算变成了现在预估高度一次性先计算,然后实际高度分步计算。正如苹果官方文档所说,减少了实际高度计算时的性能消耗,但是这种实际高度和预估高度差值的动态变化在滑动过快时可能会产生”跳跃“现象,所以此时的预估高度和真实高度越接近越好。

参考文章
http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • 我们在上一篇《通过代码自定义不等高cell》中学习了tableView的相关知识,本文将在上文的基础上,利用sto...
    啊世ka阅读 1,497评论 2 7
  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,321评论 8 265
  • 一、简介 <<UITableView(或简单地说,表视图)的一个实例是用于显示和编辑分层列出的信息的一种手段 <<...
    无邪8阅读 10,584评论 3 3
  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 8,993评论 3 38
  • 版权声明:未经本人允许,禁止转载. 1. TableView初始化 1.UITableView有两种风格:UITa...
    萧雪痕阅读 2,906评论 2 10