在不中断用户滚动的情况下将单元格添加到 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 中的数组数组中?