UICollectionView 状态恢复:自定义滚动位置

Posted

技术标签:

【中文标题】UICollectionView 状态恢复:自定义滚动位置【英文标题】:UICollectionView state restoration: customizing scroll position 【发布时间】:2015-02-03 23:01:52 【问题描述】:

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

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

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

但是,我观察到的是,实施此协议确实会在恢复期间正确影响所选单元格的 indexPath(这对我的应用程序并不重要),但它不会影响滚动位置。滚动位置(collection view 的 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 的用法?有错误吗?有没有更优雅或更正确的方法来让它工作?

【问题讨论】:

我可以看到indexPathForElementWithModelIdentifier:inView: 旨在做同样的事情。有效吗? @orkenstein 我尝试使用它,但它没有用。请参阅我的 OP:“实施此协议确实会在恢复期间正确影响所选单元格的 indexPath(这对我的应用程序并不重要),但不会影响滚动位置。” 【参考方案1】:

正如您已经指出的,UIDataSourceModelAssociation 似乎无法恢复UICollectionView 的可见偏移量,但仅适用于选定项目。我尝试在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)

【讨论】:

很好的分析。对我来说很有意义。 这行得通,但是在错误的地方稍微恢复了偏移——可能是导航栏计算出了问题。 您可以使用 encodeCGPoint() 无需包装在 NSValue 中 @stephanek。我正在重写代码如下:但它似乎根本不起作用。请问我是否遗漏了什么?谢谢。我在下面的答案中发布了格式化的代码。 @stephanek。覆盖 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 ) 让 offsetValue = coder.decodeObject(forKey: "CollectionViewOffset") 为! CGPoint collectionView?.setContentOffset(offsetValue, animated: false) 【参考方案2】:

根据@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)
    

【讨论】:

你应该使用 decodePoint,它是一个 CGPoint,而不是一个对象 :-) developer.apple.com/documentation/foundation/nscoder/… 我的天啊,我测试了 decodeCGPoint,它就像一个魅力。谢谢!!

以上是关于UICollectionView 状态恢复:自定义滚动位置的主要内容,如果未能解决你的问题,请参考以下文章

部分之间的多个自定义 TableviewCell 上的多个自定义 UICollectionView

试图在 UICollectionViewCell Swift 中覆盖“selected”以实现自定义选择状态

在 UICollectionView 中添加自定义视图

UICollectionView - 连接到自定义单元格

如何使用 2 个或更多自定义单元格创建自定义 UICollectionView?

具有自定义布局的 UICollectionView 变为空白