首先说明是什么坑:iOS11中tableView
默认开启self-sizing
,这会导致heightForRow
代理方法与cellForRow
数据源方法调用顺序的改变。
Self-Sizing在iOS11下是默认开启的,Headers, footers, and cells都默认开启Self-Sizing,所有estimated 高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension
如果目前项目中没有使用estimateRowHeight属性,在iOS11的环境下就要注意了,因为开启Self-Sizing之后,tableView是使用estimateRowHeight属性的,这样就会造成contentSize和contentOffset值的变化,如果是有动画是观察这两个属性的变化进行的,就会造成动画的异常,因为在估算行高机制下,contentSize的值是一点点地变化更新的,所有cell显示完后才是最终的contentSize值。因为不会缓存正确的行高,tableView reloadData的时候,会重新计算contentSize,就有可能会引起contentOffset的变化。
这是你可能需要为你的 APP 适配 iOS 11这篇文章中的一段。
按照这篇文章,将App中大大小小的地方处理过之后,发现聊天界面出现了奇怪的问题,如图:
部分cell出现了气泡大小不正确的问题。
经过反复排查,发现代码中气泡大小的计算是写在heightForRow
方法中,按照之前的调用顺序,先调用heightForRow
计算好气泡大小,后调用cellForRow
数据源时就可以直接取到气泡大小,这样是没有问题的。但iOS11中默认开启了self-sizing
,这样会导致这两个方法调用顺序的改变,先调用cellForRow
自然就没法取到正确的行高了。
解决方法很简单,如下
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
第一句将估算行高设为0,既关闭行高自适应自动计算行高,这样代理方法的顺序就又和之前的一样了。为了稳妥起见,又加上了后面两句关闭组头组尾的行高估算。
至于为什么开启估算行高会引起这个问题,本人的猜测是,tableView的默认行高是44.f,如果给rowHeight属性赋值或者重写了heightForRow方法,系统就会先取自定义的高度,但是一种例外就是开启了自适应行高。
恩,听上去蛮靠谱的。我们知道,UITableView 是个 UIScrollView,就像平时使用 UIScrollView 一样,加载时指定 contentSize 后它才能根据自己的 bounds、contentInset、contentOffset 等属性共同决定是否可以滑动以及滚动条的长度。而 UITableView 在一开始并不知道自己会被填充多少内容,于是询问 data source 个数和创建 cell,同时询问 delegate 这些 cell 应该显示的高度,这就造成它在加载的时候浪费了多余的计算在屏幕外边的 cell 上。
上面一段是阳神的一篇文章中的,当开启了自适应行高,系统就会去先获取到对应的cell通过计算约束或者sizeThatFits来获取cell的真实高度,然后再调用heightForRow来看是否有用户指定的行高。这样就会导致这两个方法调用顺序的变化。