You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义UICollectionViewFlowLayout应用transform后顶部cell突然消失如何解决

问题原因
  • 系统默认的layoutAttributesForElements方法只会返回原始frame落在查询矩形内的cell属性。当顶部cell滚动到原始可见区域外时,super方法不会返回它的布局属性,你无法为其设置渐变动画参数,系统会直接回收该cell,导致突然消失。
  • 现有代码未对alpha、scale做取值限制,当滚动距离过大时会出现负数,也会导致cell异常不可见。
修复后的代码
class EdgeZoomLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let collectionView = collectionView else { return nil }
        // 扩大查询范围,覆盖顶部100pt的渐隐区域,确保渐变中的cell不会被漏掉
        let extendedRect = rect.inset(by: UIEdgeInsets(top: 100, left: 0, bottom: 0, right: 0))
        let rectAttributes = super.layoutAttributesForElements(in: extendedRect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
        let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.frame.size)

        for attributes in rectAttributes {
            let positionInFrameY = attributes.center.y - visibleRect.origin.y
            let cutoff = CGFloat(30)
            
            if positionInFrameY <= cutoff {
                let translation = cutoff - positionInFrameY
                // 限制alpha在0~1之间,避免出现负数
                let alpha = max(0, min(1, 1 - (translation / 100)))
                // 限制最小缩放值,可根据需求调整下限
                let scale = max(0.8, min(1, 1 - (translation / 1000)))
                
                attributes.alpha = alpha
                // 渐变中的cell设为更高层级,避免被下方cell遮挡
                attributes.zIndex = 2
                attributes.transform = CGAffineTransform(scaleX: scale, y: scale).translatedBy(x: 0, y: translation)
            } else {
                attributes.alpha = 1
                attributes.transform = .identity
                attributes.zIndex = 1
            }
        }

        return rectAttributes
    }

    override init() { super.init() }
    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true }
    override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
        let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
        context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size
        return context
    }
}
补充说明

你可以根据自己的效果需求调整两个参数:

  • 扩大查询范围的top值:和你计算alpha时分母的100保持一致即可,确保alpha降到0之前的cell都能被查询到
  • scale的最小下限:如果不需要限制最小缩放,可以把max(0.8, ...)去掉,直接计算即可

内容的提问来源于stack exchange,提问作者aheze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 18:15:04