【问题标题】:UICollectionView animating cell beyond boundsUICollectionView 动画单元格越界
【发布时间】:2017-04-04 08:40:25
【问题描述】:

我设置了一个UICollectionView,它具有以下设置:

  • collectionView 适合屏幕边界
  • 仅应用垂直滚动
  • 大多数单元格适合内容的宽度
  • 某些单元格可以在用户交互时动态更改其高度(动画)

它很像UITableView,在大多数情况下都可以正常工作,除了动画不适用的一种特定情况。

collectionView 中堆叠的单元格中,假设上面的单元格之一扩展了它的高度。然后下部单元必须向下移动以保持距离。如果此移动单元格的目标帧超出collectionView's 边界,则不应用动画并且单元格消失。

相反的情况也是如此;如果下部单元格的源帧超出屏幕边界(当前超出边界)并且上部单元格应该缩小,则不会应用动画,它只会出现在目标帧上。

这在UICollectionView 控制的内存管理逻辑中似乎是合适的,但同时向用户显示某些内容只是突然出现或消失并不自然。我用 UITableView 对此进行了测试,并且发生了同样的事情。

是否有解决此问题的方法?

【问题讨论】:

  • 您是否每次都在使集合视图布局的上下文无效?
  • @NRitH 事实上,这是正确的。这可能是问题的原因吗?
  • @NRitH 布局无效后,我通常会调用setCollectionViewLayout:animated: 来为帧变化设置动画。
  • @Tack-GyuLee 你能添加一些代码和 gif 吗

标签: ios animation uicollectionview uicollectionviewcell uicollectionviewlayout


【解决方案1】:

您应该添加一些代码或至少是您的 UI 问题的 gif。

我尝试使用基本的UICollectionViewLayout 子类来复制您的问题:

protocol CollectionViewLayoutDelegate: AnyObject {
    func heightForItem(at indexPath: IndexPath) -> CGFloat
}

class CollectionViewLayout: UICollectionViewLayout {
    weak var delegate: CollectionViewLayoutDelegate?

    private var itemAttributes: [UICollectionViewLayoutAttributes] = []

    override func prepare() {
        super.prepare()
        itemAttributes = generateItemAttributes()
    }

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
        return collectionView?.contentOffset ?? .zero
        return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return itemAttributes.first { $0.indexPath == indexPath }
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return itemAttributes.filter { $0.frame.intersects(rect) }
    }

    private func generateItemAttributes() -> [UICollectionViewLayoutAttributes] {
        var offset: CGFloat = 0
        return (0..<numberOfItems()).map { index in
            let indexPath = IndexPath(item: index, section: 0)
            let frame = CGRect(
                x: 0,
                y: offset,
                width: collectionView?.bounds.width ?? 0,
                height: delegate?.heightForItem(at: indexPath) ?? 0
            )
            offset = frame.maxY
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = frame
            return attributes
        }
    }
}

在一个简单的UIViewController 中,每次选择一个单元格时我都会重新加载第一个单元格:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    updatedIndexPath = IndexPath(item: 0, section: 0)
    collectionView.reloadItems(at: [updatedIndexPath])
}

在那种情况下,我遇到了这样的动画:

如何解决?

我认为您可以尝试调整 super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath) 返回的属性,计算其正确的帧并使用 z-index。

但您也可以像这样简单地尝试使所有布局无效:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    layout = CollectionViewLayout()
    layout.delegate = self
    collectionView.setCollectionViewLayout(layout, animated: true)
}

并覆盖:

override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
    return collectionView?.contentOffset ?? .zero
}

避免布局失效时目标内容偏移量计算错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    相关资源
    最近更新 更多