Compositional Layout におけるレイアウト構築のおすすめ

f:id:komaji504:20200914155330p:plain:w414
Compositional Layout の2列表示

Compositional Layout を使って、上記のような一方向のみスクロールできるグリッドレイアウトを構築する場合の実装方法についてのおすすめ。

基本

制約との二重管理になってしまうので可能な限り .absolute の使用は避け、 Self-Sizing されるようにする。

スクロール方向のサイズ指定

.estimated を用いて Self-Sizing されるようにする。
例えば縦スクロールの場合は、セルの width に応じて height が変わって欲しいので item, group 両方の height に対して .estimated() を指定する。

let itemSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .estimated(estimatedItemHeight) // スクロール方向なので .estimated()
)

let groupLayoutSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .estimated(estimatedItemHeight) // スクロール方向なので .estimated()
)

スクロールと垂直方向のサイズ指定

UICollectionView のサイズによって決まるようにしたいので、.fractional(1.0) を指定する。
例えば縦スクロールの場合は、セルの width は UICollectionView の width によって決まるようにしたいので、 item, group 両方の width に対して .fractional(1.0) を指定する。

let itemSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0), // スクロールと垂直方向なので .fractionalWidth(1.0)
    heightDimension: .estimated(estimatedItemHeight)
)

let groupLayoutSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0), // スクロールと垂直方向なので .fractionalWidth(1.0)
    heightDimension: .estimated(estimatedItemHeight)
)

カラム数の指定

画像の2列表示のように複数カラムある場合は、 group の count にカラム数を指定する。
これにより、interItemSpacing を考慮してカラムサイズが自動計算されるので、 .fractional(1.0) はそのままで良くなる。

let group = NSCollectionLayoutGroup.horizontal(
    layoutSize: groupLayoutSize,
    subitem: item,
    count: 2 // 2行表示 
)
group.interItemSpacing = .fixed(8.0)

上記に沿った実装例

上記の内容をまとめると、画像のようなレイアウトを構築するには下記のような実装になる。

func makeGridSection() -> UICollectionViewLayout {
    let estimatedItemHeight: CGFloat = 100.0
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0), // スクロールと垂直方向なので .fractionalWidth()
        heightDimension: .estimated(estimatedItemHeight) // スクロール方向なので .estimated()
    )
    let item = NSCollectionLayoutItem(layoutSize: itemSize)

    let groupLayoutSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0), // スクロールと垂直方向なので .fractionalWidth()
        heightDimension: .estimated(estimatedItemHeight) // スクロール方向なので .estimated()
    )
    let group = NSCollectionLayoutGroup.horizontal(
        layoutSize: groupLayoutSize,
        subitem: item,
        count: 2 // 2行表示 
    )
    group.interItemSpacing = .fixed(8.0)

    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 8.0
    let sideInset: CGFloat = 16.0
    section.contentInsets = .init(
        top: .zero,
        leading: sideInset,
        bottom: .zero,
        trailing: sideInset
    )

    return UICollectionViewCompositionalLayout(section: section)
}