具有动态高度的 UICollectionViewLayout - 但不使用流布局

标签 uicollectionview ios12 swift5

假设您有一个带有普通自定义 UICollectionViewLayout 的 UICollectionView。
就是这样 >>> 不是 <<< 流布局 - 这是一个普通的自定义布局。
自定义布局是微不足道的,在 prepare调用你简单地走下数据并布置每个矩形。所以说它是一个垂直滚动的集合......

override func prepare() {
    cache = []
    var y: CGFloat = 0
    let k = collectionView?.numberOfItems(inSection: 0) ?? 0
    // or indeed, just get that direct from your data
    
    for i in 0 ..< k {
        
        // say you have three cell types ...
        let h = ... depending on the cell type, say 100, 200 or 300
        
        let f = CGRect(
            origin: CGPoint(x: 0, y: y ),
            size: CGSize(width: screen width, height: h)
        )
        
        y += thatHeight
        y += your gap between cells
        
        cache.append( .. that one)
    }
}
在示例中,对于上述三种单元格类型中的每一种,单元格高度都是固定的 - 一切都没有问题。
处理动态单元格高度 如果您使用的是流布局 很好的探索而且确实比较简单 . ( Example ,也可以在www上看到很多解释。)
但是,如果您想要动态单元格高度( NON -flow)完全正常的日常 UICollectionViewLayout 怎么办?
估计的ItemSize 在哪里?
据我所知,UICollectionViewLayout 中没有estimatedItemSize 概念?
那你到底在做什么?
您可以天真地 - 在上面的代码中 - 简单地以一种或另一种方式计算每个单元格的最终高度(例如计算任何文本块的高度等)。但这似乎非常低效:在计算整个 100 个单元格大小之前,可以绘制任何集合 View 。您根本不会使用任何 iOS 的动态高度功能,并且没有任何东西是及时的。
我想,您可以从头开始对整个即时系统进行编程。 (所以,像 .. 使表格大小实际上只有 1,手动计算该高度,将其发送到集合 View ;计算项目 2 的高度,将其发送,等等。)但这很蹩脚。
有什么方法可以使用自定义 UICollectionViewLayout 实现动态高度单元格 - 而不是流布局?
(同样,当然很明显你可以手动完成,所以在上面的代码中一次计算所有 1000 个高度,你就完成了,但这会很蹩脚。)
就像我在上面说的第一个难题是,(正常,非流)UICollectionViewLayout 中的“估计大小”概念到底在哪里?

最佳答案

只是一个警告:自定义布局远非微不足道,他们可能值得自己写一篇研究论文;)

可以 在您自己的布局中实现大小估计和动态调整大小。实际上,估计的尺寸没什么特别的。相反,动态大小是。因为自定义布局会给你一个 总计 控制一切,然而,这涉及许多步骤。您需要在布局子类中实现三种方法,在单元格中实现一种方法。

  • 首先,你需要实现preferredLayoutAttributesFitting(_:)在您的单元格中(或更一般地说,可重用的 View 子类)。在这里你可以使用任何你想要的计算。您可能会对单元格使用自动布局:如果是这样,您将需要将所有单元格的 subview 添加到其 contentView 中。 ,将它们约束到边缘,然后调用 systemLayoutSizeFitting(_:withHorizontalFittingPriority:verticalFittingPriority:)在这个“首选属性”方法中。例如,如果您希望单元格在垂直方向上调整大小,同时在水平方向上受到约束,您可以这样写:
    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    
                // Ensures that cell expands horizontally while adjusting itself vertically.
                let preferredSize = systemLayoutSizeFitting(layoutAttributes.size, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
    
                layoutAttributes.size = preferredSize
                return layoutAttributes
            }
    
  • 在询问单元格的首选属性后,shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:)在布局对象上将被调用。重要的是,您不能只是简单地输入 return true ,因为系统将无限期地重新询问单元格。这实际上非常聪明,因为许多单元格可能会对彼此的更改使用react,因此布局最终决定是否满足单元格的愿望。通常,为了调整大小,你会写这样的东西:
    override func shouldInvalidateLayout(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool {
    
                if preferredAttributes.size.height.rounded() != originalAttributes.size.height.rounded() {
                    return true
                }
                return false
            }
    
  • 紧接着,invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)将被调用。您通常希望自定义上下文类来存储特定于您的布局的信息。一个重要的、相当不直观的警告是你应该 不是 调用 context.invalidateItems(at:)因为这会导致布局失效 只有提供的索引路径中实际可见的那些项目。只需跳过此方法,因此布局将重新查询可见矩形。

    然而!是否需要设置contentOffsetAdjustment需要慎重考虑和 contentSizeAdjustment :如果调整大小,您的集合 View 作为一个整体可能会缩小或扩大。如果您不考虑这些,滚动时将有跳转重新加载。
  • 最后,invalidateLayout(with:)将被调用。这是您实际调整部分/行高的步骤,移动受调整单元格影响的内容等。如果您覆盖,您将需要调用 super .

  • PS:这真的是一个很难的话题,我只是触及了表面。你可以看看here它变得多么复杂(但这个 repo 也是一个非常丰富的学习工具)。

    关于具有动态高度的 UICollectionViewLayout - 但不使用流布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58250053/

    相关文章:

    ios - idleTimerDisabled 不适用于(自主)单一应用程序模式

    ios - 自定义 header UIView 到所有 VC?

    swift - 我无法将图像放入 Swift 5 中的 UIButton

    Swift UIcollectionView 在同一个 ViewController 上更改第二个 UICollectionView 的内容?

    ios - UICollectionView - 不显示单元格

    ios - Swift 4.2 无法将类型 '(UIImagePickerController.InfoKey).Type' 的值转换为预期的参数类型 'UIImagePickerController.InfoKey'

    memory-leaks - iOS 12+ 内存泄漏 - WKWebView 和 UIWebView

    flutter - 如何在共享扩展中获取shared_preferences

    ios - 为什么 UICollectionView 的 didSelect 方法不起作用?

    swift - 如何重置 UICollectionVIewCell 的背景颜色