[Swift]UICollectionViewの進化:Compositional Layouts編
経緯
iOS13から大きく変更されたCollectionViewですが、
特に影響の大きいCompositional LayoutsとDiffable Data Sourceについて、
しっかりと理解をしておきたかった為。
何が変わったのか
これまでのCollectionViewの構築方法がどう変化したか、
個人的な重要点であるLayoutとData Sourceをピックアップして紹介します。
この記事ではまずLayoutに関する変更点をまとめています。
Data Sourceに関しての記事はこちら
参考資料としているWWDCのスライド資料です。
Layout
これまで主にLayoutで使用されてきたクラスは、
UICollectionViewFlowLayoutこのクラスかと思います。
ただこのクラスでは昨今の複雑なレイアウトを作成するのに、
以下のようにいくつか課題がありました。

- Boilerplate code: ボイラープレート・コード
- Performance considerations: パフォーマンスへの配慮
- Supplementary and decoration view challenges: 補足・装飾表示の課題
- Self-sizing challenges: セルフサイジングの課題
その解決策として登場したのが、Compositional Layoutsです。
Compositional Layouts
Compositional Layoutsとは以下のように紹介されています
- Composing small layout groups together:小さなレイアウトグループをまとめて合成
- Layout groups are line-based:レイアウトグループはラインベース
- Composition instead of subclassing:サブクラス化ではなくコンポジション化
以下公式サンプルコードとスライドからCompositional Layoutsの構成を理解していきます。
// Create a List by Specifying Three Core Components: Item, Group and Section
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(44.0))
let item = NSCollectionLayoutItem(layoutSize: size)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)


Item > Group > Section > Layoutという階層構造になっており、
全体のサイズを決定しているのがサンプルコードのsizeプロパティになります。
ここからさらに細かくその構造に使用されているClassを見て理解を深めましょう。
NSCollectionLayoutSize
全てのレイアウトはサイズが明示されていて、
幅と高さの寸法から構成されます。
class NSCollectionLayoutSize {
init(widthDimension: NSCollectionLayoutDimension,
heightDimension: NSCollectionLayoutDimension)
}
じゃあその寸法(以下Dimension)はどういう構成になっているかというと、
以下のように4つの定義方法があります。
class NSCollectionLayoutDimension {
class func fractionalWidth(_ fractionalWidth: CGFloat) -> Self
class func fractionalHeight(_ fractionalHeight: CGFloat) -> Self
class func absolute(_ absoluteDimension: CGFloat) -> Self
class func estimated(_ estimatedDimension: CGFloat) -> Self
}
順に4つの違いや特徴を見ていきましょう。
fractionalWidth、fractionalHeight
画面サイズに対しての比率の割合を指定できます。

①fractionalWidth
①このスライド例では、
画面サイズに対しての比率が0.5なので画面サイズの半分のwidthとなります。

②fractionalHeight
②このスライド例では、
画面サイズに対しての比率が0.3なので画面サイズの30%のheightとなります。

③fractionalWidth + fractionalHeight
③このスライド例では、
widthとheightの両方にfractionalWidth0.25を設定しています。
そのためwidthとheightの大きさは同等となります。
absolute,estimated
fractionalWidth、fractionalHeightが画面サイズに対しての比率の割合でしたが、
absolute,estimatedはabsolute pointで値を指定できます。

absolute
absoluteの場合は固定の絶対値を指定します。
他の制約があってもこちらを優先します。

estimated
estimatedの場合も値を指定出来ますが、
他の制約があった場合は制約が優先されます。
NSCollectionLayoutItem
Compositional Layoutsの階層構造の最小部品のitemですが、
生成時に前述したNSCollectionLayoutSizeを設定する必要があります。
class NSCollectionLayoutItem {
convenience init(layoutSize: NSCollectionLayoutSize)
var contentInsets: NSDirectionalEdgeInsets
}
またitemの使い道としてはCellやヘッダーやフッターなどの補足ビューです。
ただ個人的にはヘッダーやフッターなどは、
NSCollectionLayoutBoundarySupplementaryItemで、
定義することが多いイメージを持っています。
NSCollectionLayoutGroup
groupはレイアウトの基本単位を構成します。
以下3つの形で定義することが可能になり、カスタム性が向上しました。
horizontal, vertical, custom
第2引数のsubitemsには前途したNSCollectionLayoutItemをセットします。

NSCollectionLayoutSection
sectionは図の緑の枠の部分になります。

sectionでよく使われるプロパティを、いくつか紹介します。
interGroupSpacing
セクション内でのグループ間のスペースを設定出来るプロパティです。
contentInsets
セクションのコンテンツとその境界の間のスペースを設定出来るプロパティです。
boundarySupplementaryItems
セクションの境界部分に関連する、
ヘッダーまたはフッターを追加するために使用されるオブジェクトの配列です。
なのでヘッダーまたはフッターを使用する際によく使用されます。
decorationItems
背景など、セクションに固定される装飾アイテムの配列です。
よく背景を設定する際に使用されます。
UICollectionViewCompositionalLayout NSCollectionViewCompositionalLayout
これまでItem Group Section挙げてきました。
最後にLayoutの部分となります。
initでの生成方法がに4種類あり、以下のように使い分けが出来ます。
ここで言うconfigurationは、例えばセクション間のスペースを設定したい場合など、
レイアウトのスクロール方向、セクション間隔、ヘッダまたはフッタを定義するオブジェクトです。
UICollectionViewCompositionalLayoutConfiguration
- 単一セクションの場合
init(section: NSCollectionLayoutSection) - 単一セクションかつ追加のconfigurationがある場合
init(section:NSCollectionLayoutSection,configuration:UICollectionViewCompositionalLayoutConfiguration) - 複数セクションでセクション単位でレイアウトの設定をしたい場合
init(sectionProvider: UICollectionViewCompositionalLayoutSectionProvider) - 複数セクションでセクション単位でレイアウトの設定をしたいかつ追加のconfigurationがある場合
init(sectionProvider: UICollectionViewCompositionalLayoutSectionProvider, configuration: UICollectionViewCompositionalLayoutConfiguration)
Compositional Layoutの感想
UICollectionViewFlowLayoutの課題点をうまく解消出来ていると思います。
より小さなレイアウト部分まで設定することが可能になり、
使用するAPIもほとんど統一されているので1度流れを覚えたら使いやすく感じると思います。
続き
Data Sourceに関しての記事はこちら
Discussion