插入新单元格时防止 UITableView 滚动
Prevent UITableView from scrolling when new cells are inserted
我已经看到这个问题被问过好几次了,尽管已经实施了社区提出的每一个解决方案,但我仍然没有成功。我正在实施的是一个基本的 public 聊天应用程序。我需要在 UITableView 中显示通过 API 收到的许多消息。为了有聊天的感觉,我把我的 UITableView 和 UITableViewCell 颠倒过来,把它们的 transform 属性 改成了 CGAffineTransform(scaleX: 1, y: -1)
。相反,要将单元格添加到 UITableView,我首先通过 messages.insert(message, at: indexPath.row)
将传入消息添加到数组,然后我调用 insertRows(at: [indexPath], with: animation)
(其中 indexPath 是以这种方式创建的IndexPath(row: 0, section: 0)
)。当我在 UITableView 的底部时,一切都很好:新单元格从底部到顶部出现,并伴随着流畅的动画。当我滚动到顶部几个像素时,问题就开始了。查看这些图像以更好地理解差异。
我想要实现的是防止 UITableView 滚动,除非我在它的最底部,这样,如果用户为了阅读过去的消息而滚动到顶部,他可以毫无困难地这样做由 UITableView 的移动引起。
我希望有人能指出我正确的方向。谢谢
编辑:如果有帮助,我正在使用自动 UITableViewCell 高度。
编辑:这是我当前的代码:
我正在使用通用包装器 class ListView<Cell: UITableViewCell, Item>
以及用于添加新项目的此方法:
func add(_ item: Item) {
items.insert(item, at: 0)
if contentOffset.y > -contentInset.top {
insertRows(at: [IndexPath(row: 0, section: 0)], with: .top)
} else {
reloadData()
}
}
我不得不使用 -contentInset.top
来检查我是否在滚动视图的最底部,因为我之前出于布局原因将 contentInset 设置为 UIEdgeInsets(top: composeMessageView.frame.height - 4, left: 0, bottom: 4, right: 0)
。同样,我将 estimatedRowHeight 设置为 44,将 rowHeight 设置为 UITableViewAutomaticDimension。
func add(_ item: Item) {
// Calculate your `contentOffset` before adding new row
let additionalHeight = tableView.contentSize.height - tableView.frame.size.height
let yOffset = tableView.contentOffset.y
// Update your contentInset to start tableView from bottom of page
updateTableContentInset()
items.append(item)
// Create indexPath and add new row at the end
let indexPath = IndexPath(row: objects.count - 1, section: 0)
tableView.insertRows(at: [indexPath], with: .top)
// Scroll to new added row if you are viewing latest messages otherwise stay at where you are
if yOffset >= additionalHeight {
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
}
这里是更新的方法contentInset
。它会给你带来与你通过 CGAffineTransform(scaleX: 1, y: -1)
达到的效果相同的效果
func updateTableContentInset() {
var contentInsetTop = tableView.frame.size.height - tableView.contentSize.height
if contentInsetTop <= 0 {
contentInsetTop = 0
}
tableView.contentInset = UIEdgeInsets(top: contentInsetTop, left: 0, bottom: 0, right: 0)
}
我已经看到这个问题被问过好几次了,尽管已经实施了社区提出的每一个解决方案,但我仍然没有成功。我正在实施的是一个基本的 public 聊天应用程序。我需要在 UITableView 中显示通过 API 收到的许多消息。为了有聊天的感觉,我把我的 UITableView 和 UITableViewCell 颠倒过来,把它们的 transform 属性 改成了 CGAffineTransform(scaleX: 1, y: -1)
。相反,要将单元格添加到 UITableView,我首先通过 messages.insert(message, at: indexPath.row)
将传入消息添加到数组,然后我调用 insertRows(at: [indexPath], with: animation)
(其中 indexPath 是以这种方式创建的IndexPath(row: 0, section: 0)
)。当我在 UITableView 的底部时,一切都很好:新单元格从底部到顶部出现,并伴随着流畅的动画。当我滚动到顶部几个像素时,问题就开始了。查看这些图像以更好地理解差异。
我想要实现的是防止 UITableView 滚动,除非我在它的最底部,这样,如果用户为了阅读过去的消息而滚动到顶部,他可以毫无困难地这样做由 UITableView 的移动引起。
我希望有人能指出我正确的方向。谢谢
编辑:如果有帮助,我正在使用自动 UITableViewCell 高度。
编辑:这是我当前的代码:
我正在使用通用包装器 class ListView<Cell: UITableViewCell, Item>
以及用于添加新项目的此方法:
func add(_ item: Item) {
items.insert(item, at: 0)
if contentOffset.y > -contentInset.top {
insertRows(at: [IndexPath(row: 0, section: 0)], with: .top)
} else {
reloadData()
}
}
我不得不使用 -contentInset.top
来检查我是否在滚动视图的最底部,因为我之前出于布局原因将 contentInset 设置为 UIEdgeInsets(top: composeMessageView.frame.height - 4, left: 0, bottom: 4, right: 0)
。同样,我将 estimatedRowHeight 设置为 44,将 rowHeight 设置为 UITableViewAutomaticDimension。
func add(_ item: Item) {
// Calculate your `contentOffset` before adding new row
let additionalHeight = tableView.contentSize.height - tableView.frame.size.height
let yOffset = tableView.contentOffset.y
// Update your contentInset to start tableView from bottom of page
updateTableContentInset()
items.append(item)
// Create indexPath and add new row at the end
let indexPath = IndexPath(row: objects.count - 1, section: 0)
tableView.insertRows(at: [indexPath], with: .top)
// Scroll to new added row if you are viewing latest messages otherwise stay at where you are
if yOffset >= additionalHeight {
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
}
这里是更新的方法contentInset
。它会给你带来与你通过 CGAffineTransform(scaleX: 1, y: -1)
func updateTableContentInset() {
var contentInsetTop = tableView.frame.size.height - tableView.contentSize.height
if contentInsetTop <= 0 {
contentInsetTop = 0
}
tableView.contentInset = UIEdgeInsets(top: contentInsetTop, left: 0, bottom: 0, right: 0)
}