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

时间:2018-01-30 19:26:13

标签: ios uitableview

我正在构建一个简单的聊天用户界面到我的应用程序中,但是在添加单元格时我无法滚动。

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

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

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

我已经阅读了无数关于此的帖子,我已经尝试了所有提议的解决方案(contentOffset reset,estimatedRowHeight缓存,reloadData,insertRow等),但似乎没有相当的工作。

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

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

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

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

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

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

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

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

我很想知道别人为此找到了什么解决方案。这在纸面上似乎是一个微不足道的问题,但我已经在这里待了很多天,没有明确的解决方案。

目前解决这个问题的最佳做法是什么?特别适用于表格视图,其中(动态,可变高度,自我调整大小)单元格在用户滚动时位于顶部。

我提前感谢您提供的任何提示!

1 个答案:

答案 0 :(得分:0)

我最终选择了一个效果很好的解决方案。我会在这里发布,以防将来帮助其他人。仍然接受其他答案。

基本上我发现当前在视图框顶部可见的消息,我发现它偏移到顶部。然后当数据加载时我再次找到该消息,找到它的新索引路径并滚动到那里以及偏移量。这样可以将用户准确地放在他们所在的位置(从视觉上讲,当然,当加载单元格时,用户在技术上会进一步向下列表)。

我使用这个新解决方案向链接的github项目发布了一个提交(它在" Attempt#5",只是取消注释并注释掉Attempt#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
}