UICollectionView 不遵守 UICollectionViewLayoutAttributes 的 zIndex

UICollectionView not obeying zIndex of UICollectionViewLayoutAttributes

我想要达到的效果是一种粘性 header 单元格。粘性细胞漂浮在其他细胞之上对我来说很重要。有点像这样:

┌──────────┐ 
│          │ 
│  Cell 0  │ 
│          ├┐
└┬─────────┘│
 │  Cell 4  │
 │          │
 └──────────┘
 ┌──────────┐
 │          │
 │  Cell 5  │
 │          │
 └──────────┘
 ┌──────────┐
 │          │
 │  Cell 6  │
 │          │
 └──────────┘

单元格 4、5 和 6 通常是可见的,我在 layoutAttributesForElementsInRect: 期间在我的 UICollectionViewFlowLayout 子类中构建单元格 0 的属性。我所做的就是调用超级实现,确定我需要添加的单元格,然后构造 UICollectionViewLayoutAttributes(forCellWithIndexPath:)。然后我将它的 zIndex 设置为 1(默认为 0)。

我遇到的问题是 UICollectionView 似乎总是忽略 zIndex

┌──────────┐ 
│          │ 
│  Cell 0  │ 
│┌─────────┴┐
└┤          │
 │  Cell 4  │
 │          │
 └──────────┘
 ┌──────────┐
 │          │
 │  Cell 5  │
 │          │
 └──────────┘
 ┌──────────┐
 │          │
 │  Cell 6  │
 │          │
 └──────────┘

现在我相信可以使用 3d 变换从视觉上解决这个问题,但这对我不起作用,因为我不希望任何点击进入顶部的单元格。因此,在此示例中,我不希望 Cell 4 接收用于 Cell 0 的抽头。

有人有什么想法吗?这是在 iOS 8.4.

我做了一个独立的项目来去除我所有无关的代码。在这个过程中我想我找到了自己的解决方案。

override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?

override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?

我们也没有应用新的 zIndex 所以我认为这是造成它的原因。

只是觉得我应该 post 支持解决方案,以防万一在搜索其他人时出现这种情况。

在 UICollectionViewLayout 中设置 zIndex 对我不起作用

我在 UICollectionViewController 的方法中设置了 zIndex

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
customCell.layer.zPosition = 0;
}

我最好的猜测是布局和控制器将 zIndex 设置为奇怪的东西

您需要同时设置这两个值。 zIndex3DTransform 参数。

class HeaderUnderCellsLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let attributesArray = super.layoutAttributesForElements(in: rect) else { return nil }

        attributesArray.forEach { attributes in
            if attributes.representedElementCategory == .supplementaryView && attributes.representedElementKind == UICollectionView.elementKindSectionHeader {
                attributes.transform3D = CATransform3DMakeTranslation(0, 0, -10)
                attributes.zIndex = -200
            }
        }
        return attributesArray
    }

    override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath)
        attributes?.transform3D = CATransform3DMakeTranslation(0, 0, -10)
        attributes?.zIndex = -200
        return attributes
    }
}

由于 UIKit 错误,无法更新 UICollectionViewLayoutAttributes.zIndex

这段代码可以解决问题:

// In your UICollectionViewCell subclass:
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
    super.apply(layoutAttributes)
    layer.zPosition = CGFloat(layoutAttributes.zIndex) // or any zIndex you want to set
}

我在 UICollectionViewLayout 的子类中遇到了类似的问题,其中单元格的记录不考虑对 UICollectionViewLayoutAttributes.zIndex 的更新,但我能够通过重新加载移动的单元格来解决该问题。

我特别用了这个:

    collectionView.performBatchUpdates({
        collectionView.moveItem(at:indexPath, to:newIndexPath)
    })

    collectionView.reloadItems(at: [indexPath, newIndexPath])

似乎这种情况仍在 iOS 14 上发生。这就是我在我的案例中设法修复它的方法:

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
    {
        guard let attributes = collectionView.collectionViewLayout.layoutAttributesForItem(at: indexPath) else
        {
            assert(false)
            return
        }
        cell.layer.zPosition = CGFloat(attributes.zIndex)
    }
    func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath)
    {
        guard let attributes = collectionView.collectionViewLayout.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) else
        {
            assert(false)
            return
        }
        view.layer.zPosition = CGFloat(attributes.zIndex)
    }