UITableView常见问题的解决方案

一、刷新时界面跳动“乱跑”的现象

1.原因

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 会往上跳动乱跑』的现象。

2.解决方案

方案一: 在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
    }
 }

二、设置sectionHeader或sectionFooter的高度不生效

1.UITableView的两种样式:
  • 普通样式UITableView.Style.plain(默认样式): 默认没有tableHeaderView,tableFooterView,sectionHeaderView,sectionFooterView。
  • 分组样式UITableView.Style.grouped:
    使用默认的高度设置tableHeaderView,tableFooterView,sectionHeaderView,sectionFooterView。
2.使用属性sectionHeaderHeight 和 sectionFooterHeight 设置 header 或 footer 的高度

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。

3.使用代理方法设置 header 或 footer 的高度

case1: 当tableView的样式是 .plain 时,可以使用代理方法设置header或footer高度

case2: 当tableView的样式是 .grouped 时,无法使用代理方法设置header或footer的高度。

三、层次结构UITableViewWrapperView导致cell跑偏

在tableView上,还有一个UITableViewWrapperView,UITableViewWrapperView之上才是我们的UITableViewCell。有时我们会遇到UITableViewWrapperView 的frame位置和tableView不同,导致cell跑偏,可以采用以下办法进行解决:

if #available(iOS 11.0, *) {
    tableView.contentInsetAdjustmentBehavior = .never
}else {
    self.automaticallyAdjustsScrollViewInsets = false
}

将自适应 autolayout 关闭。

你可能感兴趣的:(iOS学习,ios,swift)