首页 > 解决方案 > 当我在屏幕上的 indexPaths 上方插入行时,UITableView 会跳起来

问题描述

我正在尝试在我的表格视图中添加一些行。当插入行高于屏幕上的行时,表格视图会向上跳。当我在上面插入行时,我希望我的表格视图保持在它已经在的位置。请记住: tableView 跳转到它显示的 indexPath,但是在上面添加行之后,底部行 indexPaths 会发生变化,并且新的第 n 个 indexPath 是别的东西。

标签: iosswiftuitableviewuiscrollview

解决方案


This is unfortunately not as easy task as one would think. Table view jumps when you add a cell on top because the offset is persisted and cells updated. So in a sense it is not the table view that jumps, cells jump since you added a new one on top which makes sense. What you want to do is for your table view to jump with the added cell.

I hope you have fixed or computed row heights because with automatic dimensions things can complicate quite a bit. It is important to have the same estimated height as actual height for row. In my case I just used:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 72.0
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return 72.0
}

Then for testing purposes I add a new cell on top whenever any of the cells is pressed:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    var offset = tableView.contentOffset.y
    cellCount += 1
    tableView.reloadData()
    let paths = [IndexPath(row: 0, section: 0)]
    paths.forEach { path in
        offset += self.tableView(tableView, heightForRowAt: path)
    }
    DispatchQueue.main.async {
        tableView.setContentOffset(CGPoint(x: 0.0, y: offset), animated: false)
    }
}

So I save what the current offset of the table view is. Then I modify the data source (My data source is just showing number of cells). Then simply reload the table view.

I grab all the index paths that have been added and I modify the offset by adding the expected height of every added cell.

At the end I apply the new content offset. And it is important to do that in the next run loop which is easies done by dispatching it asynchronously on main queue.

As for automatic dimensions.

I would not go there but it should be important to have size cache.

private var sizeCache: [IndexPath: CGFloat] = [IndexPath: CGFloat]()

Then you need to fill the size cache when cell disappears:

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    sizeCache[indexPath] = cell.frame.size.height
}

And change the estimated height:

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return sizeCache[indexPath] ?? 50.0
}

Also when modifying your offset you need to use estimated height:

paths.forEach { path in
    offset += self.tableView(tableView, estimatedHeightForRowAt: path)
}

This worked for my case but automatic dimensions are sometimes tricky so good luck with them.


推荐阅读