swift - scrollViewDidScroll 用奇怪的 contentOffsetY 调用

标签 swift uitableview core-data nsfetchedresultscontroller

我有一个包含 NSFetchedResultsControllerUIViewController。在将行插入到顶部之后,我想让这些行在插入之前保持可见。这意味着我需要进行一些计算以在更新后立即保留 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 (提交 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,则委托(delegate)已被调用。调用委托(delegate)方法的原因是什么?有什么办法让它不滚动吗?

最佳答案

我下载了您的代码并进行了一些调整以解决故障问题。这是我所做的。

  1. 将 TableView 的 estimatedRowHeight 属性设置为某个数字
    init() {
        super.init(frame: .zero, style: .plain)
        estimatedRowHeight = 50.0
        register(MyTableViewCell.self, forCellReuseIdentifier: "cell")
    }
  1. 创建了一个函数来处理 UITableView 重新加载操作而不修改当前的 contentOffset
    func reloadDataWithoutScroll() {
        let lastScrollOffset = contentOffset
        beginUpdates()
        reloadData()
        layoutIfNeeded()
        endUpdates()
        layer.removeAllAnimations()
        setContentOffset(lastScrollOffset, animated: false)
    }
  1. 更新了 controllerDidChangeContent 函数以使用 reloadDataWithoutScroll 函数
    UIView.performWithoutAnimation {
        tableView.performBatchUpdates({
            tableView.insertRows(at: inserts, with: .automatic)
        })   
        inserts.removeAll()
        tableView.reloadDataWithoutScroll()
    }

当我执行这些更改时,添加新行时它不会滚动。但是,当当前 contentOffset 为 0 时,它会滚动以显示新添加的行。从逻辑上讲,我认为这不是问题。

关于swift - scrollViewDidScroll 用奇怪的 contentOffsetY 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55315281/

相关文章:

ios - View Controller 无法访问数组中的元素

ios - 如何在 Parse.com 中使用 Sql "Select"for Swift

iphone - 如何为 UITableViewCell 创建弹出窗口,就像 iPhone 上的音乐应用程序 (iOS 5)

ios - 如何在 Swift 中编辑动态 UITableViewCell 中的 View ?

ios - 在执行核心数据迁移时如何获取关系实体?

ios - 在 CoreData 中存储枚举属性并使其方便

ios - 在 xcode 中从一个 View Controller 拖动到另一个 View Controller 时,"show as popover"没有出现?

swift - 使用变量作为键访问 Swift 字典

ios - 重新加载 UITableView 导致标签设置不正确

ios - 是否可以实现自己的 IASKSettingsReader?