为什么从远程服务器加载更多行时,具有自动调整行高的UITableView会“先滚动然后向下滚动”?

Ahm*_*edr 5 uitableview ios swift4

UITableView每行的标签长度都有变化,因此在我的viewDidLoad代码中包含以下代码,以根据其内容自动调整表视图行的大小:

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
Run Code Online (Sandbox Code Playgroud)

对于固定的行数,这绝对是可以的。但就我而言,行数有时会变大,因此我一次显示15行会更快一些。

在这里,我从UITableViewDelegate方法中请求远程服务器提供更多数据tableView(_:willDisplay:forRowAt:)

问题是,当我插入一组新的行时,表视图有点上下摇晃,并且在加载新项之前,用户会迷失在表中的位置。

我将新行插入表视图的方式如下:

func insertRowsInSection(_ tableView: UITableView, newObjects: [Object]) {
    var indexPaths = [IndexPath]()
    let lastRowPlusOne = tableView.numberOfRows(inSection: 0)

    if newObjects.count >= 1 {
        for objectIndex in 0...newObjects.count - 1 {
            let indexPath = IndexPath(row: objectIndex + lastRowPlusOne, section: 0)
            indexPaths.append(indexPath)
        }

        if #available(iOS 11.0, *) {
            tableView.performBatchUpdates({
                tableView.insertRows(at: indexPaths, with: .automatic)
            }, completion: nil)
        } else {
            tableView.beginUpdates()
            tableView.insertRows(at: indexPaths, with: .automatic)
            tableView.endUpdates()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我必须说,将行高固定为特定值时,根本没有这种“抖动”行为。

tpe*_*dyy 5

这是与UITableView一起自动调整单元格大小的问题。UITableView会根据您的估算高度来计算它的contentSize。例如。

tableView.estimatedRowHeight = 100
Run Code Online (Sandbox Code Playgroud)

这意味着如果您有15行,则“估计的” tableView的contentSize将约为1500px +(其他可能类似于节标题),但是在滚动传递它们之后,将根据显示的单元格重新计算实际的contentSize。那你打电话的时候

tableView.insertRows(at: indexPaths, with: .automatic)
Run Code Online (Sandbox Code Playgroud)

它将通过使用那些estimateSize(也将通过estimateSize计算新添加的单元格)将动画执行到contentSize,这意味着这不是精确的计算,会导致跳动的动画。

最好的解决方案是...。手动计算高度,并通过UITableViewDelegate返回确切的单元格高度

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
Run Code Online (Sandbox Code Playgroud)

这将需要做更多的工作,并且会破坏使用UITableViewCell进行自动布局的所有目的。但这将为您提供UITableView上非常精确的插入/删除/重新加载动画。您仍然可以在单元格内部使用自动布局,但是还可以手动计算元素的边距或文本大小。

另一个解决方法是缓存单元格高度。

var cellHeights: [IndexPath: CGFloat] = [:]

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    guard let height = cellHeights[indexPath] else {
        return UITableViewAutomaticDimension
    }
    return height
}

func tableView(_ tableView: UITableView,  willDisplay cell: UITableViewCell,  forRowAt indexPath: IndexPath) {
    cellHeight[indexPath] = cell.frame.height
}
Run Code Online (Sandbox Code Playgroud)

这将为您提供解决方法,但这是100%动画问题的解决方案。如果我想使用漂亮的平滑tableView动画,我仍然会进行手动高度计算。