如何使用组合布局为不同的部分设置不同的背景?

How can I set different backgrounds for different sections using compositional layout?

我希望使用组合布局为集合视图中的不同部分设置不同的背景。

目前我正在使用 NSCollectionLayoutDecorationItem.background 设置一个部分的背景。然而,这种类型的视图不会像补充视图或单元那样以相同的方式出列或回收。初始化后无法更改,也无法自己初始化。

我想使用作为我从后端检索的用户数据的一部分的颜色或背景图像,因此在编译时我不知道颜色或图像。

有什么方法可以动态设置某个部分的背景图片或颜色吗?

经过一番努力,我想出了一个办法来做到这一点。就其本身而言,它并不太 hacky,但需要额外的 hack 来解决看似 UIKit 错误的问题。

注意事项

  1. 只有当您的部分内容和部分 header 的高度都是绝对的并且提前知道(至少在部分布局提供程序中)时,这才有效。

    这在我的案例中有效,因为我的 header 和单元格都是固定高度,而且我每个部分只使用一行 horizontally-scrolling 单元格。

  2. 背景视图不得水平滚动(假设 vertically-scrolling collection 视图。

    在我的例子中,我希望固定部分背景图像,单元格在它们上方水平(正交)滚动。

Constraints/Assumptions

  1. 我们只能使用组合布局(在我的例子中是为了获得简单的正交滚动)。

  2. 设置背景视图的功能(在问题中提到)将无法使用,因为它不能 通过补充视图注册或任何其他方式配置 per-section。 所以一定要用正规的补充品。

  3. 补充项目必须在该部分才能获得正确的尺寸,这 意味着只使用 NSCollectionLayoutBoundarySupplementaryItem.

    3.1。虽然可以欺骗不同的类型(例如组中的 non-boundary 补充项目) 进入正确的大小和位置,使用技巧导致 UIKit 不 知道背景何时真正可见,这会导致闪烁 in/out 滚动期间。

  4. 尽管附加到节,节补充项目将是 仅相对于单元格垂直定位,忽略header部分 部分。

  5. 更糟糕的是,这种对齐方式与设置高度等于总截面相结合 高度导致补充项目拉伸部分的高度 在单元格下方,使该部分太高。

  6. 有一个为补充项设置位置偏移的工具, 但是由于我只能假设是一个错误(即使偏移量为 0), 使用它会导致部分 header 高度折叠为零,从而导致 下面的细胞向上移动并覆盖它

解决方案

因此,为了解决所有这些问题,我所做的是:

  1. 将背景补充项目大小设置为已知高度header + cells/row + 它们之间的任意间距。
  2. 通过垂直偏移 header 将背景项目拉到 header 下方 header 高度(使用偏移量会导致单元格向上移动;请参阅约束 6)。
  3. 通过填充顶部插入部分的高度将单元格向下推 header (hackInsets).
  4. 将背景的 leading/trailing 边缘拉到 view/screen 边缘,以 使它成为 full-bleed (通常它会像内容一样插入)。我们这样做 在后台的背景图像视图上设置负边距 补充观看注册。
  5. 在背景视图上设置一个负数 z-index 以将其保留在单元格下方 header。

在代码中,相关部分如下所示:

let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(Self.headerHeight))
let headerSupplementaryItem = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: ElementKind.header, alignment: .top)

let hackInsets = NSDirectionalEdgeInsets(top: Self.headerHeight, leading: 0, bottom: 0, trailing: 0)
let backgroundHeight = MySectionHeaderView.nominalSize.height + Self.interItemSpacing + MyCollectionViewCell.nominalSize.height
let backgroundOffset = CGPoint(x: 0, y: -MySectionHeaderView.nominalSize.height)
let backgroundAnchor = NSCollectionLayoutAnchor(edges: .top, absoluteOffset: backgroundOffset)
let backgroundSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(backgroundHeight))
let backgroundSupplementaryItem = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: backgroundSize, elementKind: ElementKind.background, containerAnchor: backgroundAnchor)
backgroundSupplementaryItem.zIndex = -1

let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
section.contentInsets = Self.sectionInsets.summedWith(hackInsets)
section.boundarySupplementaryItems = [headerSupplementaryItem, backgroundSupplementaryItem]