scrollViewDidScroll 用奇怪的 contentOffsetY 调用
scrollViewDidScroll called with strange contentOffsetY
我有一个 UIViewController
,里面有一个 NSFetchedResultsController
。在将行插入到顶部之后,我想让这些行在插入之前保持可见。这意味着我需要进行一些计算以在更新后立即保留 contentOffSetY。计算是正确的,但我注意到 scrollViewDidScroll
在滚动到我指定的 contentOffsetY 后被调用,这导致状态损坏。这是日志记录:
Will apply an corrected Y value of: 207.27359771728516
Scrolled to: 207.5
Corrected to: 207.27359771728516
Scrolled to: 79.5 <-- Why is this logline here?
您可以直接克隆一个示例项目:https://github.com/Jasperav/FetchResultControllerGlitch (commit https://github.com/Jasperav/FetchResultControllerGlitch/commit/d46054040139afeeb648e1e0b5b113bd98685b4a,最新版本的项目只有一些问题,对 scrollViewDidScroll 方法的奇怪调用现在已经消失了。如果您修复了故障,我将奖励赏金。只需克隆最新版本 运行 并稍微滚动一下。你会看到奇怪的内容偏移(故障))。 运行 项目,你会看到奇怪的输出。这是 controllerDidChangeContent
方法:
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
let currentSize = tableView.contentSize.height
UIView.performWithoutAnimation {
tableView.endUpdates()
let newSize = tableView.contentSize.height
let correctedY = tableView.contentOffset.y + newSize - currentSize
print("Will apply an corrected Y value of: \(correctedY)")
tableView.setContentOffset(CGPoint(x: 0,
y: correctedY),
animated: false)
print("Corrected to: \(correctedY)")
}
}
如果我在 tableView.endUpdates()
之后立即调用 tableView.layoutIfNeeded
,则委托已被调用。调用委托方法的原因是什么?有什么办法让它不滚动吗?
我下载了您的代码并进行了一些调整以解决故障问题。这是我所做的。
- 将 table 视图的
estimatedRowHeight
属性 设置为某个数字
init() {
super.init(frame: .zero, style: .plain)
estimatedRowHeight = 50.0
register(MyTableViewCell.self, forCellReuseIdentifier: "cell")
}
- 创建了一个函数来处理 UITableView 重新加载操作而不修改当前的 contentOffset
func reloadDataWithoutScroll() {
let lastScrollOffset = contentOffset
beginUpdates()
reloadData()
layoutIfNeeded()
endUpdates()
layer.removeAllAnimations()
setContentOffset(lastScrollOffset, animated: false)
}
- 更新了
controllerDidChangeContent
函数以使用 reloadDataWithoutScroll
函数
UIView.performWithoutAnimation {
tableView.performBatchUpdates({
tableView.insertRows(at: inserts, with: .automatic)
})
inserts.removeAll()
tableView.reloadDataWithoutScroll()
}
当我执行这些更改时,添加新行时它不会滚动。但是,当当前 contentOffset 为 0 时,它会滚动显示新添加的行。从逻辑上讲,我认为这不是问题。
我有一个 UIViewController
,里面有一个 NSFetchedResultsController
。在将行插入到顶部之后,我想让这些行在插入之前保持可见。这意味着我需要进行一些计算以在更新后立即保留 contentOffSetY。计算是正确的,但我注意到 scrollViewDidScroll
在滚动到我指定的 contentOffsetY 后被调用,这导致状态损坏。这是日志记录:
Will apply an corrected Y value of: 207.27359771728516
Scrolled to: 207.5
Corrected to: 207.27359771728516
Scrolled to: 79.5 <-- Why is this logline here?
您可以直接克隆一个示例项目:https://github.com/Jasperav/FetchResultControllerGlitch (commit https://github.com/Jasperav/FetchResultControllerGlitch/commit/d46054040139afeeb648e1e0b5b113bd98685b4a,最新版本的项目只有一些问题,对 scrollViewDidScroll 方法的奇怪调用现在已经消失了。如果您修复了故障,我将奖励赏金。只需克隆最新版本 运行 并稍微滚动一下。你会看到奇怪的内容偏移(故障))。 运行 项目,你会看到奇怪的输出。这是 controllerDidChangeContent
方法:
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
let currentSize = tableView.contentSize.height
UIView.performWithoutAnimation {
tableView.endUpdates()
let newSize = tableView.contentSize.height
let correctedY = tableView.contentOffset.y + newSize - currentSize
print("Will apply an corrected Y value of: \(correctedY)")
tableView.setContentOffset(CGPoint(x: 0,
y: correctedY),
animated: false)
print("Corrected to: \(correctedY)")
}
}
如果我在 tableView.endUpdates()
之后立即调用 tableView.layoutIfNeeded
,则委托已被调用。调用委托方法的原因是什么?有什么办法让它不滚动吗?
我下载了您的代码并进行了一些调整以解决故障问题。这是我所做的。
- 将 table 视图的
estimatedRowHeight
属性 设置为某个数字
init() {
super.init(frame: .zero, style: .plain)
estimatedRowHeight = 50.0
register(MyTableViewCell.self, forCellReuseIdentifier: "cell")
}
- 创建了一个函数来处理 UITableView 重新加载操作而不修改当前的 contentOffset
func reloadDataWithoutScroll() {
let lastScrollOffset = contentOffset
beginUpdates()
reloadData()
layoutIfNeeded()
endUpdates()
layer.removeAllAnimations()
setContentOffset(lastScrollOffset, animated: false)
}
- 更新了
controllerDidChangeContent
函数以使用reloadDataWithoutScroll
函数
UIView.performWithoutAnimation {
tableView.performBatchUpdates({
tableView.insertRows(at: inserts, with: .automatic)
})
inserts.removeAll()
tableView.reloadDataWithoutScroll()
}
当我执行这些更改时,添加新行时它不会滚动。但是,当当前 contentOffset 为 0 时,它会滚动显示新添加的行。从逻辑上讲,我认为这不是问题。