NSCollectionLayoutGroup 之子视图的填充
本篇主要讲解 NSCollectionLayoutGroup
内 “子视图” 的填充方式。
算是为 UICollectionViewCompositionalLayout
的讲解做一些铺垫,毕竟把 Item 的填充方式了解清楚了之后,才能组合多种 Group 来进行更复杂的布局。
NSCollectionLayoutGroup
不考虑方向的话,有两种填充子视图的方法。
概览
通过头文件您能查到如下几个初始化方法:
open class func vertical(layoutSize: NSCollectionLayoutSize, subitems: [NSCollectionLayoutItem]) -> Self
@available(iOS, introduced: 13.0, deprecated: 16.0)
open class func vertical(layoutSize: NSCollectionLayoutSize, subitem: NSCollectionLayoutItem, count: Int) -> Self
@available(iOS 16.0, *)
open class func vertical(layoutSize: NSCollectionLayoutSize, repeatingSubitem subitem: NSCollectionLayoutItem, count: Int) -> Self
@available(iOS, introduced: 16.0, deprecated: 16.0, renamed: "vertical(layoutSize:repeatingSubitem:count:)")
public class func verticalGroup(with size: NSCollectionLayoutSize, repeatingSubitem subitem: NSCollectionLayoutItem, count: Int) -> NSCollectionLayoutGroup
第三个方法是 iOS 16 新出的,同时废弃了第二个方法,而第四个方法在 iOS 16 引入同时又在 iOS 16 废除。
后文将围绕前两个方法展开讨论,不涉及到后两个方法。
subitem + count
这种初始化方式将在每个 Group 中重复 count 个 item。使用这种方法进行初始化时,在不同的方向上,layoutSize
会有不同的表现:
vertical
func vertical(layoutSize: NSCollectionLayoutSize, subitem: NSCollectionLayoutItem, count: Int) -> NSCollectionLayoutGroup
在纵向上,NSCollectionLayoutItem
的 widthDimension
生效,但是heightDimension
将被忽略。
Item 的实际高度会是 .fractionalHeight(1/count)
。
假设现在有下面这些代码:
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: .absolute(100))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(100))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 2)
此时 Item 的实际高度会是 50,相当于一个高度为 100 的 Group 内有2个高度为 50 的 Item。
horizontal
func horizontal(layoutSize: NSCollectionLayoutSize, subitem: NSCollectionLayoutItem, count: Int) -> NSCollectionLayoutGroup
和 vertical 时的情况相对应:
在横向上,NSCollectionLayoutItem
的 heightDimension
生效,但是widthDimension
将被忽略。
Item 的实际宽度会是 .fractionalWidth(1/count)
。
这里就不再用代码举例说明了。
subitems
和上一种初始化方法相比,使用 subitems
的方式来进行初始化要简单的多,因为它内部的 Item 的大小完全取决于 Item 自身。考虑如下代码:
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: .absolute(100))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(300))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
该示例中,每个 Item 的高度就是 100,而宽度是 Group 的 1/3。每个 Group 中有多少个 Item 取决于数据源传递给 UICollectionView
的数量。
有一点需要注意的是,Group 只是用来辅助布局的,它本身并不会生成任何视图。所以尽管在本示例中它的高度只有 300,不足 UICollectionView
的高度,但是 Item 依然会充满整个 UICollectionView
,而不是仅仅在高度为 300 的范围内进行滑动。
我并不能肯定这种现象可以称之为 “忽略 Group 的 layoutSize”,所以没有使用这种表述。
总结
这两种初始化方式各有各的用途:
在不考虑嵌套布局(Group 中嵌套 Item 和 Group)的情况下,这两种布局的使用场景大体上一致,只需要注意各自对 Item 大小的计算规则即可。绝大多数情况下我自己会选择 subitems
的初始化方式,因为 Item 的大小计算规则更为直观。
但是在嵌套布局的前提下,这两种初始化方式就会有比较大的差异。
首先嵌套布局的最外层只能使用 subitems
的方式初始化,数组中包含多种不同的 Item 或者 Group。
其次如果需要指定 Item 的数量,那么就只能使用 subitem + count
的初始化形式。
考虑下图的场景:
如果现在要求顶部的标题、中间的工具和下方的灰色分隔条,三部分放到一个 NSCollectionLayoutGroup
对象中,可以使用 [标题Item + 工具Group + 灰条Item]
的组合布局形式。
此时 “工具Group” 就不能使用 subitems
的方式进行初始化了,必须要指定工具的数量,否则 UICollectionView
会把下方的灰条也融到工具 Group 中。