在不中断用户滚动的情况下将单元格添加到 UITableView 的顶部

Posted

技术标签:

【中文标题】在不中断用户滚动的情况下将单元格添加到 UITableView 的顶部【英文标题】:Adding cells to top of UITableView without interrupting user scrolling 【发布时间】:2018-01-30 19:26:12 【问题描述】:

我正在我的应用中构建一个简单的聊天 UI,但在添加单元格时滚动时遇到问题。

当用户在顶部附近向上滚动时,我会触发后台加载以获取更多聊天消息。当这些到达时,我将它们放在前面(将它们添加到表格视图的顶部)。

我看到的问题是当新单元格进入时,表格视图一直滚动到顶部。

我想要的是,当添加新单元格时,用户的位置和滚动方向/速度不会被打断。

我已经阅读了无数关于此的帖子,并且尝试了所有建议的解决方案(contentOffset 重置、estimatedRowHeight 缓存、reloadData、insertRow 等),但似乎没有一个完全有效。

大多数解决方案都适用于将单元格添加到表格视图底部的情况。这很好用,因为contentOffset 不会在您reloadData 时受到影响。但是,当将单元格添加到顶部时,无法知道添加的所有新单元格的新总高度。

我应该提到我正在使用具有动态高度的自定尺寸单元格。单元格包含可能有多行长的动态文本。

为了消除我的应用程序其余部分可能存在的任何怪癖,我创建了一个简单的精简应用程序来说明问题。

您可以在 github 上查看并自行尝试:github.com/nebs/pagination-scroll-test。

该应用程序只是模拟了一个无限的虚假消息列表。当您一直滚动到顶部时,它会加载并预先添加更多虚假消息(模拟延迟 2 秒)。所有代码都在ViewController.swift

您可以忽略大部分样板代码。主要感兴趣的部分是in this line at the reloadTableView() function。

我拥有的“最佳”(迄今为止)解决方案是尝试 #4(参见代码)。在这里,我只记得最上面呈现的消息,然后再次滚动到该消息。它有点工作,但如果用户在中间滚动,它会有点抖动,因为它不会准确滚动到用户所在的位置。

我的代码中的其他 3 次尝试不起作用(添加新单元格后表格视图跳转到顶部)。

我很想知道其他人为此找到了哪些解决方案。这在纸上似乎是一个微不足道的问题,但我已经解决了很多天,但没有看到明确的解决方案。

目前解决此问题的最佳做法是什么?特别适用于在用户滚动时在顶部添加(动态、可变高度、自定大小)单元格的表格视图。

提前感谢大家提供的任何提示!

【问题讨论】:

self.tableView.scrollToRow(at: IndexPath(row: self.pageSize, section: 0), at: .top, animated: false)不是更简单,这样您就不必搜索消息了。最后一个索引路径将始终位于索引pageSize,其中pageSize 是最后一次提取的计数。 @beyowulf 因此,如果用户在单元格加载之前一直位于表格视图的顶部,则此方法有效。但是,如果用户触发加载但随后向下滚动到某个地方,那么当新单元格进入时,它会跳回到上一页的顶部,如果这有意义的话。我希望找到一个似乎不会中断用户当前滚动位置/速度的解决方案。 (注意在我的代码中,我抓取了visible 索引路径,这给了我最显眼的消息,而不是最顶层的整体消息) 您正在调用 reloadData(),这显然会从 tableView 中删除所有单元格并重新填充它们。简单来说,tableView 之前状态的所有东西现在都没有了,你现在有了一个全新的 tableView。有选择地插入新添加的单元格是值得的。检查这个答案***.com/a/31870301/1880899 @SumitAnantwar 看看我在 github 中链接的项目,我有一些不同的尝试(已注释掉)。我尝试了所有这些,包括使用 insertRow 插入单元格(请参阅:github.com/nebs/pagination-scroll-test/blob/master/ScrollTest/…)即使我调用 insertRow 并且只插入新单元格,它仍然会滚动到顶部。除非我错过了什么? 【参考方案1】:

我最终选择了一个效果很好的解决方案。我会在这里发布它以防将来对其他人有所帮助。仍然对其他答案持开放态度。

基本上,我找到了当前在视图框架顶部可见的消息,并找到了它到顶部的偏移量。然后,当数据加载时,我再次找到该消息,找到它的新索引路径并与偏移量一起滚动到那里。这具有将用户准确定位到他们所在位置的效果(从视觉上讲,当然,当加载单元格时,从技术上讲,用户在列表中的位置更靠后)。

我使用这个新解决方案发布了对链接的 github 项目的提交(它位于“尝试 #5”下,只需取消注释该位并注释掉尝试 #1”即可对其进行测试。)

    var currentMessage: Message? = nil
var offsetFromTopOfMessage: CGFloat = 0
if let topIndexPath = self.tableView.indexPathsForVisibleRows?.first 
    var topMessageRect = self.tableView.rectForRow(at: topIndexPath)
    topMessageRect = topMessageRect.offsetBy(dx: -tableView.contentOffset.x, dy: -tableView.contentOffset.y)
    offsetFromTopOfMessage = topMessageRect.minY
    currentMessage = self.messages[topIndexPath.row]

self.generateMoreMessages()
self.tableView.reloadData()
if let targetMessage = currentMessage,
    let targetIndex = (self.messages.index$0.title == targetMessage.title) 
    let targetIndexPath = IndexPath(row: targetIndex, section: 0)
    self.tableView.scrollToRow(at: targetIndexPath, at: .top, animated: false)
    self.tableView.contentOffset.y -= offsetFromTopOfMessage

【讨论】:

以上是关于在不中断用户滚动的情况下将单元格添加到 UITableView 的顶部的主要内容,如果未能解决你的问题,请参考以下文章

如何在不破坏响应性的情况下将 <div> 锚定到表格单元格的左下角?

如何在不滚动的情况下将项目添加到 ListView 的开头?

如何在不引用其他单元格的情况下将报表从 Reporting Services 2005 导出到 Excel?

如何在不破坏我的结构的情况下将特定单元格排除到 BigQuery 中的数组数组中?

如何在不破坏滚动功能的情况下将 UIImage 添加到 UIScrollView?

我可以在不膨胀/重新膨胀布局的情况下将视图添加到滚动视图吗?