6

我正在尝试为元素可能四处移动的 UICollectionView 找到处理状态恢复的最佳方法。我的目标是确保在重新启动应用程序时,集合视图中最后查看的项目仍然可见,即使项目已经移动。例如,当应用程序被终止时,项目 A 位于索引 3 的单元格中,并且当应用程序重新启动时,如果模型说项目 A 应该显示在索引 4 处,我希望集合视图将偏移量初始化为索引 4 处的单元格.

正如文档所述,我认为在我的班级中实现UIDataSourceModelAssociation协议会为我解决这个问题:UICollectionViewDataSource

[UITableView 和 UICollectionView] 类使用该协议的方法来确保相同的数据对象(而不仅仅是相同的行索引)被滚动到视图中并被选中。

但是,我观察到的是,实现此协议确实会在恢复期间正确影响所选单元格的 indexPath (这对我的应用程序并不重要),但不会影响滚动位置。滚动位置(集合视图的 contentOffset)始终恢复到应用程序被终止时的确切位置,并且不受 UICollectionViewDataSource 的影响。

我确实有一个看起来像这样的解决方法。它与模型关联协议的模式基本相同,但我必须手动完成:

override func encodeRestorableStateWithCoder(coder: NSCoder) {
    let identifier = determineIdOfCurrentlyVisibleCell()
    coder.encodeObject(identifier, forKey: "visibleCellIdentifier")
}

override func decodeRestorableStateWithCoder(coder: NSCoder) {
    if let identifier = coder.decodeObjectForKey("visibleCellIdentifier") as? String {
        if let indexPath = model.indexPathForIdentifier(identifier) {
            collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .CenteredVertically, animated: false)
        }
    }
}

我是否误解了 UIDataSourceModelAssociation 的用法?有错误吗?有没有更优雅或更正确的方法来让它工作?

4

2 回答 2

5

正如您已经指出的那样,UIDataSourceModelAssociation似乎不适用于恢复 aUICollectionView的可见偏移量,而仅适用于选定的项目。我尝试在两者上设置断点,modelIdentifierForElementAtIndexPathindexPathForElementWithModelIdentifier注意到它们仅在我选择了一个单元格后才被调用。如果我在后台运行我的应用程序之前清除了我的集合视图的选定单元格,那么modelIdentifierForElementAtIndexPath就不会被调用,但是一旦我将至少一个单元格设置为选中,它就会被调用。至少我可以验证你不是唯一看到这种行为的人。

我认为由于UICollectionView它的不同性质,创建将可见单元格滚动到正确点的行为可能并不简单,但这显然没有反映在 Apple 的文档中。手动将标识符编码到布局的第一个可见单元格应该是一个不错的选择。我正在做的是将集合视图的滚动偏移量包装在一个NSValue并恢复它:

var collectionView: UICollectionView?

// ...

override func encodeRestorableStateWithCoder(coder: NSCoder) {
    if let view = collectionView, offsetValue = NSValue(CGPoint: view.contentOffset) {
        coder.encodeObject(offsetValue, forKey: CollectionViewContentOffsetKey)
    }

    super.encodeRestorableStateWithCoder(coder)
}

override func decodeRestorableStateWithCoder(coder: NSCoder) {
    if let offsetValue = coder.decodeObjectForKey(CollectionViewContentOffsetKey) as? NSValue {
        collectionView?.setContentOffset(offsetValue.CGPointValue(), animated: false)
    }

    super.decodeRestorableStateWithCoder(coder)
}
于 2015-04-12T21:34:18.337 回答
0

根据@stepane 的建议使用 CGPoint 进行更新。

  override func encodeRestorableState(with coder: NSCoder) {
        super.encodeRestorableState(with: coder)
        coder.encode(collectionView.contentOffset, forKey: "CollectionViewContentOffset")
    }

    override func decodeRestorableState(with coder: NSCoder) {
        super.decodeRestorableState(with: coder)
        let offsetValue = coder.decodeObject(forKey: "CollectionViewOffset") as! CGPoint
        collectionView?.setContentOffset(offsetValue, animated: false)
    }
于 2020-02-18T11:43:14.920 回答