Apple在iOS 8 中引入Self-Sizing,我们可以通过实现 estimatedRowHeight 相关的属性来展示动态的内容,实现了 estimatedRowHeight 属性后,得到的初始 contentSize 是个估算值,是通过 estimatedRowHeight * cell 的个数得到的,并不是最终的 contentSize,tableview 不会一次性计算所有的 cell 的高度,只会计算当前屏幕能够显示的 cell 个数在加上几个,滑动时,tableview 不停地得到新的 cell,更新自己的 contentSize,在滑倒最后的时候,会得到正确的 contentSize。
利用Self-Sizing技术,我们不需要实现heightForRowAt方法,系统通过AutoLayout约束自动计算cell高度并绘制显示。
在iOS 11 之后,UITableView 的 Headers、Footers 和 Cells都默认开启了 Self-Sizing,所以 estimated 高度默认值从iOS11之前的 0 改变为了 UITableViewAutomaticDimension(-1),如果项目中没有使用 estimatedRowHeight 属性,在 iOS 11 的环境下就要注意了,因为在估算行高机制下,contentSize 的值是一点点地变化更新的,所有的cell显示完后才是最终 contentSize 值,因为不会缓存正确的行高,tableView reloadData 的时候,会重新计算 contentSize,就有可能会引起 contentOffset 的变化,所以就会造成一种『下拉刷新的时候,cell 会往上跳动乱跑』的现象。
方案一: 在iOS11下如果不想使用Self-Sizing的话,可以通过以下方式关闭,即可解决刷新跳动问题
lazy var tableView: UITableView = {
let tableView = UITableView.init()
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0
return tableView;
}()
方案二:存储预估高度
定义存储预估高度的map:
var heightMap = [Int: CGFloat]()
增加实现 estimatedHeightForRowAt 和 willDisplay 的逻辑,分别获取和设置 heightMap,即可解决UITableView刷新跳动问题。
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if let value = heightMap[indexPath.row] {
return value
} else {
return 100.0
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let key = indexPath.row
if let _ = heightMap[key] {
} else {
heightMap[key] = cell.frame.size.height
}
}
- 普通样式UITableView.Style.plain(默认样式): 默认没有tableHeaderView,tableFooterView,sectionHeaderView,sectionFooterView。
- 分组样式UITableView.Style.grouped:
使用默认的高度设置tableHeaderView,tableFooterView,sectionHeaderView,sectionFooterView。
case1: 若tableView的样式为 .plain 时则不会起作用,需要将tableView的样式设置为.grouped
case2: 如果同时实现了代理方法
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.001
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.001
}
则代理方法会覆盖属性。
case3: 当tableView的样式为 .grouped 时想设置header或footer的高度为0时,只能使用属性设置。
使用代理方法不起作用,系统会使用默认高度设置header或footer。
case1: 当tableView的样式是 .plain 时,可以使用代理方法设置header或footer高度
case2: 当tableView的样式是 .grouped 时,无法使用代理方法设置header或footer的高度。
在tableView上,还有一个UITableViewWrapperView,UITableViewWrapperView之上才是我们的UITableViewCell。有时我们会遇到UITableViewWrapperView 的frame位置和tableView不同,导致cell跑偏,可以采用以下办法进行解决:
if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = .never
}else {
self.automaticallyAdjustsScrollViewInsets = false
}
将自适应 autolayout 关闭。