🍎

[Swift]UICollectionViewの進化:Compositional Layouts編

2022/03/12に公開

経緯

iOS13から大きく変更されたCollectionViewですが、
特に影響の大きいCompositional LayoutsDiffable Data Sourceについて、
しっかりと理解をしておきたかった為。

何が変わったのか

これまでのCollectionViewの構築方法がどう変化したか、
個人的な重要点であるLayoutData Sourceをピックアップして紹介します。
この記事ではまずLayoutに関する変更点をまとめています。
Data Sourceに関しての記事はこちら
参考資料としているWWDCのスライド資料です。
https://devstreaming-cdn.apple.com/videos/wwdc/2019/215wh1hurdxwcctfc8/215/215_advances_in_collection_view_layout.pdf?dl=1

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の構成を理解していきます。

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

全てのレイアウトはサイズが明示されていて、
幅と高さの寸法から構成されます。

NSCollectionLayoutSize
class NSCollectionLayoutSize {
 init(widthDimension: NSCollectionLayoutDimension,
 heightDimension: NSCollectionLayoutDimension)
}

じゃあその寸法(以下Dimension)はどういう構成になっているかというと、
以下のように4つの定義方法があります。

NSCollectionLayoutDimension
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を設定する必要があります。

NSCollectionLayoutItem
class NSCollectionLayoutItem {
 convenience init(layoutSize: NSCollectionLayoutSize)
 var contentInsets: NSDirectionalEdgeInsets
}

またitemの使い道としてはCellやヘッダーやフッターなどの補足ビューです。
ただ個人的にはヘッダーやフッターなどは、
NSCollectionLayoutBoundarySupplementaryItemで、
定義することが多いイメージを持っています。

NSCollectionLayoutGroup

groupはレイアウトの基本単位を構成します。
以下3つの形で定義することが可能になり、カスタム性が向上しました。
horizontal, vertical, custom
第2引数のsubitemsには前途したNSCollectionLayoutItemをセットします。

NSCollectionLayoutSection

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


https://developer.apple.com/documentation/uikit/nscollectionlayoutsection
sectionでよく使われるプロパティを、いくつか紹介します。

interGroupSpacing

セクション内でのグループ間のスペースを設定出来るプロパティです。
https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199093-intergroupspacing

contentInsets

セクションのコンテンツとその境界の間のスペースを設定出来るプロパティです。
https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199090-contentinsets

boundarySupplementaryItems

セクションの境界部分に関連する、
ヘッダーまたはフッターを追加するために使用されるオブジェクトの配列です。
なのでヘッダーまたはフッターを使用する際によく使用されます。
https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199089-boundarysupplementaryitems

decorationItems

背景など、セクションに固定される装飾アイテムの配列です。
よく背景を設定する際に使用されます。
https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199091-decorationitems

UICollectionViewCompositionalLayout   NSCollectionViewCompositionalLayout

これまでItem Group Section挙げてきました。
最後にLayoutの部分となります。
https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout
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