刷新后停止 Diffable 数据源滚动到顶部

Stop Diffable Data Source scrolling to top after refresh

如何在应用快照后停止可差异数据源将视图滚动到顶部。我目前有这个...

    fileprivate func configureDataSource() {
        self.datasource = UICollectionViewDiffableDataSource<Section, PostDetail>(collectionView: self.collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, userComment: PostDetail) -> UICollectionViewCell? in
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PostDetailCell.reuseIdentifier, for: indexPath) as? PostDetailCell else { fatalError("Cannot create cell")}
            
            cell.user = self.user
            cell.postDetail = userComment
            cell.likeCommentDelegate = self
            return cell
        }
        
        var snapshot = NSDiffableDataSourceSnapshot<Section, PostDetail>()
        snapshot.appendSections([.main])
        snapshot.appendItems(self.userComments)
        self.datasource.apply(snapshot, animatingDifferences: true)
    }

    fileprivate func applySnapshot() {

        //let contentOffset = self.collectionView.contentOffset
        var snapshot = NSDiffableDataSourceSnapshot<Section, PostDetail>()
        snapshot.appendSections([.main])
        snapshot.appendItems(self.userComments)
        self.datasource.apply(snapshot, animatingDifferences: false)
        //self.collectionView.contentOffset = contentOffset
    }

存储偏移量,然后重新应用它。有时它工作得很好,有时视图跳转。有更好的方法吗?

此问题的根源可能是您的项目标识符类型 - UserComment。

可区分数据源使用您的项目标识符类型的哈希来检测它是新实例还是当前表示的旧实例。 如果您手动实现 Hashable 协议,并且使用每当初始化该类型的新实例时生成的 UUID,这会误导 Diffable 数据源并告诉它这是项目标识符的新实例。所以必须删除以前的,并代表新的。这会导致 table 或集合视图在应用快照后滚动。 要解决这个问题,请将 uuid 替换为您知道唯一的类型的属性之一,或者更普遍地使用一种技术为相同的实例生成相同的哈希值。

所以总而言之,一般的想法是将具有相同哈希值的项目标识符的实例传递给快照,以告诉 Diffable 数据源这些项目不是新的,不需要删除以前的项目和插入这些。这样你就不会遇到不必要的卷轴了。

首先:在大多数情况下,@Amirrezas 的回答将是问题的正确原因。在我的例子中,导致问题的不是项目,而是部分标识符。那是具有正确值的 HashableIdentifiable,但它是 class,因此从未调用哈希函数。我花了一段时间才发现这个问题。更改为结构(并因此采用一些东西;))对我的情况有所帮助。

Apple-Dev 论坛上的 link 主题供参考:https://developer.apple.com/forums/thread/657499

希望我的回答对大家有所帮助:)

从iOS 15

开始
dataSource.applySnapshotUsingReloadData(snapshot, completion: nil)

重置 UI 以反映快照中数据的状态,而不计算差异或动画更改

您认为以下任何一种方法都行得通:

https://developer.apple.com/documentation/uikit/uicollectionviewdelegate/1618007-collectionview https://developer.apple.com/documentation/uikit/uicollectionviewlayout/1617724-targetcontentoffset

但是(就我而言)他们没有。您可能会从中受益更多,我正在使用自定义 UICollectionViewCompositionalLayout

做一些疯狂的事情

所做的开始工作是在我的自定义布局中手动设置偏移量class:

override func finalizeCollectionViewUpdates() {
    if let offset = collectionView?.contentOffset {
        collectionView?.contentOffset = targetContentOffset(forProposedContentOffset: offset)
    }
    super.finalizeCollectionViewUpdates()
}

我也覆盖并定义了 targetContentOffset(我先尝试了这个,但没有用,我认为在这里使用它是最干净的。我怀疑你是否定义了 targetContentOffset在上面的布局中不覆盖它的委托也可以工作,但是你已经需要一个自定义布局才能做到这一点所以它都是一样的。)