【iOS14】UICollectionView WWDC2020 アップデートまとめ

※　この記事は2020/7/9時点の内容を元に作成しています。
※　2020/7/9 ListsのSwipeをハンドリングする設定が`UICollectionViewListCell`から`UICollectionLayoutListConfiguration`に移動しました。
※ 2020/8/23 `textLayoutGuide`について追記しました。

# 今回もたくさんアップデートがありました

WWDC2019に引き続き
WWDC2020でも`UICollectionView`には
様々な新しいAPIが追加されました。

- Sectionベースでの`UICollectionView`の構築
- `UITableView`のようなListsの登場
- Reusableで豊富なdefault設定とカスタマイズが可能なConfigurationの登場 

などがあります。

今回はその内容についてまとめてみます。

参照にしたセッションは下記になります。

Advances in UICollectionView
https://developer.apple.com/videos/play/wwdc2020/10097

Advances in diffable data sources
https://developer.apple.com/videos/play/wwdc2020/10045

Lists in UICollectionView
https://developer.apple.com/videos/play/wwdc2020/10026

Modern cell configuration
https://developer.apple.com/videos/play/wwdc2020/10027

Build for iPad
https://developer.apple.com/videos/play/wwdc2020/10105

# アップデートの概要

まず今回の更新内容の概要を見ていきます。

## `UICollectionView`の構成要素

`UICollectionView`は

- Data 
- Layout
- Presentation

の3つから構成されます。

iOS13以前は

- Data -> `UICollectionViewDataSource`
- Layout -> `UICollectionViewLayout`(`UICollectionViewFlowLayout`)
- Presentation -> `UICollectionViewCell` and `UICollectionReusableView`

が使用されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/522c60cf-41f7-9f0a-f70c-cc3d3a4dd121.png)

そして

`UICollectionViewDiffableDataSource`
`UICollectionViewCompositionalLayout`

が登場したことで
この構成に変化がありました。

- Data -> Diffable Data Source
- Layout -> Compositional Layout
- Presentation -> UICollectionViewCell and UICollectionReusableView

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/1f74b98a-33fe-16dc-bf1a-cd427b13bfe5.png)

※
`UICollectionViewDiffableDataSource`と
`UICollectionViewCompositionalLayout`に関して
今回詳細は割愛させていただきます。

もし気になる方は以前書いた記事などを参考にしていただけますと幸いです。
https://qiita.com/shiz/items/a6032543a237bf2e1d19

そして今回の更新では
これらの要素の上に新しいAPIの機能が追加されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/c8233b01-fd07-ea00-41b6-8e3abbc0b967.png)

## Diffable Data Source

まずDiffable Data Sourceに関してです。

### Section Snapshot

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/fc652213-2cdb-0e06-f676-41e95b629cd7.png)


既存の`NSDiffableDataSourceSnapshot`は
`UICollectionView`全体のUIの状態を表現するものでしたが
こちらはSectionごとのUIの状態を表現します。

https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesectionsnapshot

`NSDiffableDataSourceSnapshot`に似ていますが
いくつか異なる点があります。

#### append

https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesectionsnapshot/3600716-append


appendは`NSDiffableDataSourceSnapshot`にもありますが
注目なのは第二引数です。

```swift
mutating func append(_ items: [ItemIdentifierType], 
                     to parent: ItemIdentifierType? = nil)
```


これはラベルからもわかるように
表示するData間で**親子関係**を構築することができます。

これによってSection内で階層を表現して
階層構造を持ったリストのようなViewを簡単に構築することができるようになりました。

※
これは今年のアップデートで
Outlineベースと呼ばれるUIを
様々なアプリで採用しているという背景があり
これを実現するためにも必要な機能であったようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/fc1f20b4-9433-b298-b578-d796a4203568.png)

#### expand collapse isExpanded

上記のような階層構造をもったSection内で
子要素の折り畳み、展開の管理を
フレームワーク側で管理できるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/3fb4ec72-433f-aa5d-8b4a-9afd16ae4cb2.png)

https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesectionsnapshot/3600721-expand
https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesectionsnapshot/3600717-collapse
https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesectionsnapshot/3600729-isexpanded

このメソッドを利用することで
フレームワーク側で表示やSnapshotの状態を自動で更新してくれます。

またユーザの操作でViewが変更された時のハンドリングを行うための
新しいstructも登場しました。(内容は詳細で記載します。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/1608b4c0-6c5b-ce93-a9c0-6975af05648d.png)

https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/sectionsnapshothandlers

### Reordering Support

ユーザがセルの並び替えを行った際のハンドリングが
非常に簡単にできるようになりました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/0e0fd935-95e8-65e2-646f-d354c45ce858.png)

主にこのstructを利用します。
https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/3600966-sectionsnapshothandlers

さらに
詳細は後ほど記載しますが
Swiftの`CollectionDifference`のAPIを併せて利用することで
自身が管理しているデータとUIの状態の同期が
非常に簡単にできます。

※
`CollectionDifference`に関して
今回詳細は割愛させていただきます。

もし気になる方は以前書いた記事などを参考にしていただけますと幸いです。
https://qiita.com/shiz/items/0e363219a0151d790d03


## Lists（Compositional Layout)

次にCompositional Layoutです。
今回の更新はListsの登場が中心になります。

### Listsの特徴

- `UITableView`のような外見
- スワイプアクションなど`UITableView`で用意されていた機能を利用できる
- 多くの共通レイアウトが用意されている
- Compositional Layoutの上で構成されている
- Self-Sizingのサポート

### 少ないコードの記載で簡単なレイアウトが実現できる

具体的には下記のようなコードを描きます。
※ 様々な詳細は後ほど記載します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/2964567c-9a0b-75bd-cfe8-c2ec1a6fbee1.png)

この2行を記載することで
様々なデフォルトの設定が行われ
下記のようなレイアウトが構築できます。

`UICollectionLayoutListConfiguration`は
今回新しく登場したstructで
Compositional Layoutと一緒に
UITableViewのようなレイアウトを作成する時に
非常に役立ちます。

`appearance`にレイアウトの種類を設定するだけで
`UITableView`が持つ標準レイアウトを作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/fa6d2114-c8d3-97b4-0db0-67570578adc1.png)

https://developer.apple.com/documentation/uikit/uicollectionlayoutlistconfiguration
https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout/3600951-list

この他にも様々なクラスやメソッドが追加されたことで
デフォルトの設定をそのまま利用できる上に
カスタマイズも比較的自由にできるような設計になっています。

### Cell Registration 

カスタムなセルを利用する場合に
下記のようなコードを見たことがあるかと思います。

```swift

guard let cell = tableView.dequeueReusableCell(
                     withIdentifier: "Cell", for: indexPath) as? Cell else {
    fatalError()
}
```

この場合

- 文字列でセル名を指定しなければならない
- キャストをする必要がある
- 失敗した場合の処理が必要になる

などのちょっと使いづらい点がありました。

これを解消するために
ExtensionやProtocolを実装していた方もいると思います。

今回新しく`Cell Registration`というstructが登場し
下記のような形で書けるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/0109691d-b690-b1e5-aa7b-cdd6520944cc.png)


これによって文字列によるセル名の宣言やキャストをする必要がなくなり
より型安全に実装ができるようになりました。

https://developer.apple.com/documentation/uikit/uicollectionview/cellregistration
https://developer.apple.com/documentation/uikit/uicollectionview/3600945-dequeueconfiguredreusablecell

さらにクロージャ内でセルの設定も行えるため
セルの登録と設定が同じ場所で可能になります。

### Content Configuration

新しく`UIListContentConfiguration`というstructが追加されたことで
これまでとレイアウトの方法が大きく変わります。

https://developer.apple.com/documentation/uikit/uilistcontentconfiguration

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/4204220d-2e90-000c-8623-5a2bc6097e86.png)

上記のように`UIListContentConfiguration`から
デフォルトのレイアウトを持ったインスタンスを取得し
このインスタンスに対して値を設定していきます。

そして最後にセルにこの設定を適用しています。

一見するとこれまでと変わらないかもしれませんが
実は大きな違いがあります。

これまでは`UITableViewCell`や`UICollectionViewCell`に
直接設定していたことで
同じような設定をする必要があったとしても
個々に値を設定しなければなりませんでした。

しかし
`UIListContentConfiguration`が導入されることによって
`UITableViewCell`や`UICollectionViewCell`
さらに他の`UIView`に対しても共通の設定を適用することができるようになり
再利用可能なレイアウトが実装しやすくなりました。


`UIListContentConfiguration`は
様々なデフォルトの設定を用意しており
呼び出す種類を変更することで
多様なレイアウトを実現できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/ee5a881b-772a-1d85-425e-6e3163513a99.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/c92b7d0d-7bbc-27ce-cfb9-ec43c6eda47e.png)

### Background Configuration

Content Configurationの他に
Background Configurationがあります。

これはセルのbackgroundに関連した設定を行う時に使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/80154377-ce7e-edfc-474e-02200e1bb26a.png)

https://developer.apple.com/documentation/uikit/uibackgroundconfiguration

# 各構成要素のアップデート

ここまで今年のアップデートの概要を紹介してきました。
ここからは各構成要素のアップデートを見ていきます。

# Diffable Data Source

## Section Snapshot

まずSection Snapshotです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/5cb9f790-8169-3b14-2ae5-eead9cf71519.png)


### なぜ導入されたのか

2つの理由が挙げられてました。

- セクション単位のサイズでComposableに構築できるようにしたい
- OutlineスタイルのUIを実現するために階層構造を構築したい


Outlineスタイルとは
下記のようなレイアウトを指します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/dfdfc801-d3e8-e260-a334-75714cd2bf9b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/cf7b5bd6-28dc-46a3-17b7-2fd5e9321c32.png)


Section Snapshotは
`Hasable`に適合したTypeであれば
どんなTypeも使用することができます。

また概要の方でも言及しましたが
`append`で親子関係を構築することができるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/6042778e-d726-ec61-b443-a8941588c58b.png)

`UICollectionViewDiffableDataSource`には
新しい`apply`メソッドが追加され
Section Snapshotを引数で受け取れるようになりました。

`snapshot(for section: Section)`メソッドで
Section SnapShotを取得することもできます。

`apply(_:to:animatingDifferences:completion:)`
https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/3600964-apply

`snapshot(for section: Section)`
https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/3600967-snapshot

### 適用方法

次にどのようにSection Snapshotを適用するかを見ていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/5b128011-0a7e-80ce-4b2f-638b1f151f4b.png)

大事なポイントとして
Sectionの配列の順番が表示される順番になります。

そして
各Section SnapshotにItemを追加し
`UICollectionViewDiffableDataSource`の`apply`メソッドから
CollectionViewに適用していきます。

## Expansion State

次に折り畳み、展開の機能を見ていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/7c66601d-8afe-0af0-8d9c-662cae0f4fd0.png)

Section Snapshotは
現在自身が折りたたまれているのかどうかの状態を保持しています。

これらのAPIをプログラム内で利用することで
Sectionを折り畳んだり展開したりすることが可能です。

`isExpanded`で現在の状態を取得することもできます。
つまり自身でexpansionの状態を管理する必要がありません。

一点注意が必要なのは
Snapshotの状態を変更した時点では画面の表示は変わらず
dataSourceに適用して初めて表示が変更されます。

### ユーザのアクションで表示が変更された場合は？

`SectionSnapshotHandlers`を使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/997b59e8-1934-c6ac-413c-c4c8567d61ba.png)

https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/sectionsnapshothandlers

OutlineのUI(Section Snapshot内で階層構造構築しているUI)で
新しく追加されたOutline Disclosure Accessoryを利用すると(後ほど紹介します)
フレームワーク側が内部の状態を更新してdataSourceへ適用してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/64f4b2fd-13f2-5eb6-dc08-13226561759f.png)

そしてその際に
`willExpandItem`
`willCollapseItem`
といったメソッドが呼ばれます。

さらに

`snapshotForExpandingParent`を活用することで
必要な時に必要な分だけデータをロードすることができます。

これはappendする際に指定したparentがexpandされた際に呼び出されます。
そのタイミングでデータをロードし
更新したSnapshotを返却することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/950190eb-edf1-03d6-ea9f-769c8fc4c1ec.png)

```swift

// Allow every item to be collapsed
dataSource.sectionSnapshotHandlers.shouldCollapseItem = { item in return true }

dataSource.sectionSnapshotHandlers.snapshotForExpandingParent = {
    parent, existingSnapshot -> NSDiffableDataSourceSectionSnapshot<String> in
    
    // Return child snapshot for the parent, or just existingSnapshot
}
```

https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/sectionsnapshothandlers/3600961-snapshotforexpandingparent


※　
このドキュメント記載を見る限り
expansionを有効にするためは明示的にtrueに設定する必要があるようです。

## Reordering

さらにReorderingも
フレームワーク側で管理をしてくれるようになりました。

※
こちらは`UICollectionViewDiffableDataSource`へのExtensionの追加ですが
メインはSectionへの実装になりますのSection Snapshotの中で記載させていただきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/b6d7fa0c-94c8-995a-fea0-9bc74320f163.png)

Diffable Data SourceはItemをIdentifierで管理しており
これを活用することでユーザの操作でそのアイテムが移動されたがわかります。

そのためユーザの操作に併せて
内部の状態の更新も行ってくれます。

一方で自身が管理しているデータ（いわゆるBacking Store）の更新（並べ替え）も
必要になります。

そこで今回新しく追加された
`ReorderingHandler`を活用します。

https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/reorderinghandlers

この中の`didReorder`で並び替えの結果を取得することができます。
その結果は`NSDiffableDataSourceTransaction`に入っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/911e4a4e-3ad7-7e50-356c-2efe4fbd1bd4.png)

https://developer.apple.com/documentation/uikit/nsdiffabledatasourcetransaction

`initialSnapshot`には移動前の状態が
`finalSnapshot`には移動後の状態が入っており
`difference`には移動前後で変化のあったItemの一覧が入っています。

`NSDiffableDataSourceTransaction`にはCollectionView全体の状態が含まれており
個々のSectionに関しては`NSDiffableDataSourceSectionTransaction`に含まれております。

https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesectiontransaction

ドキュメントによると下記のようにハンドリングするようです。

```swift

// Allow every item to be reordered
dataSource.reorderingHandlers.canReorderItem = { item in return true }

// Option 1: Update the backing store from a CollectionDifference
dataSource.reorderingHandlers.didReorder = { [weak self] transaction in
    guard let self = self else { return }
    
    if let updatedBackingStore = self.backingStore.applying(transaction.difference) {
        self.backingStore = updatedBackingStore
    }
}

// Option 2: Update the backing store from the final item identifiers
dataSource.reorderingHandlers.didReorder = { [weak self] transaction in
    guard let self = self else { return }
    
    self.backingStore = transaction.finalSnapshot.itemIdentifiers
}
```

Option1は自身が管理しているデータが
`CollectionDifference`のAPIを扱える場合
`CollectionDifference`を適用するだけで同期が完了します。

Option２では
またデータ自身をidentifierとして使用している場合は
`finalSnapshot`の`identifier`をそのまま適用できます。

# Lists(Compositional Layout)

次にListsです。

再喝になりますが

Listsには

- `UITableView`のような外見
- スワイプアクションなど`UITableView`で用意されていた機能を利用できる
- 多くの共通レイアウトが用意されている
- Compositional Layoutの上で構成されている
- Self-Sizing

などの特徴があります。

### Self-Sizingがデフォルト

中でもSelf-Sizingがデフォルトの挙動となっているため
自身でセルの高さを計算する必要はないと
Appleは言っております。

一方で独自で計算したい場合は
`preferredLayoutAttributesFittingAttributes`をoverrideすることで
実現することができるようです。
https://developer.apple.com/documentation/uikit/uicollectionreusableview/1620132-preferredlayoutattributesfitting


## UICollectionLayoutListConfiguration

Listsの中で重要な役割を担っているのが
`UICollectionLayoutListConfiguration`です。

https://developer.apple.com/documentation/uikit/uicollectionlayoutlistconfiguration

`UICollectionLayoutListConfiguration`は
`NSCollectionLayoutSection`の上に構築されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/519599c5-ece6-fc82-7a88-53a0375dbe89.png)

例えば下記のように使用した場合

```swift

let configuration = UICollectionLayoutListConfiguration(appearance: .sidebar)
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
```

各Sectionに同じレイアウトを適用することができます。

一方で

```swift

let layout = UICollectionViewCompositionalLayout() { sectionIndex, layoutEnvironment in
    
    var configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
    configuration.headerMode = sectionIndex == 0 ? .supplementary : .none
    
    let section = NSCollectionLayoutSection.list(using: configuration,
                                                 layoutEnvironment: layoutEnvironment)
    
    return section
}
```

とすることで
個々のSectionにそれぞれのレイアウトを適用することも可能です。


ここから
より詳細を見ていきます。


## Appearance

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/e3b12ba5-9f8a-22b5-f25b-b3c393d17769.png)

Listsではこれまで`UITableView`で実現していたレイアウトと
同じようなレイアウトを実現するためのオプションを用意しています。

- `.plain`
- `.grouped`
- `.insetGroup`

さらにiPadOS14で登場したマルチコラムのレイアウト実現するために
新しく下記の2つのオプションも用意しています。

- `.sidebar`
- `.sidebarPlain`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/c3a98a0c-b09f-0bbe-376a-e47928d4d6a7.png)

https://developer.apple.com/documentation/uikit/uicollectionlayoutlistconfiguration/appearance


## Separators Headers Footers 

さらに
Listsでは
`Separators` `Headers` `Footers`の表示方法などを
詳細にコントロールすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/af3c1970-700a-fab8-07bb-4fe706fa7ed7.png)

### Headers Footers

`UITableView`と異なる点として
HeaderやFooterは明示的にOnにしないと表示されません。

設定方法は2つがあります。

- `supplementaryView`として使用する
- （Headerのみ）`firstItemInSection`として使用する

https://developer.apple.com/documentation/uikit/uicollectionlayoutlistconfiguration/headermode
https://developer.apple.com/documentation/uikit/uicollectionlayoutlistconfiguration/footermode

#### supplementaryView

下記の例では
Headerを`supplementaryView`として使用しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/80f6a4cd-7be1-88af-df12-81c7679f6b5e.png)

この場合
画面にレンダリングされる際にViewを提供する
実装をする必要があります。

一番簡単な方法としては
Diffable Data Sourceの
`supplementaryViewProvider`を実装することです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/ec0b15b8-a530-612e-58e2-ec7d0ee149f9.png)

ただし
`UICollectionViewDelegate`を使っても
実装することができます。

### （注意）各セクションにHeaderの設定が必要

１点気をつけなければならない点として
Headerを使用する場合
使用しないSectionにも設定が必要になります。

これを行わずnilをreturnすると
assertionエラーが起きます。

そのため下記のように`.none`を明示的に設定する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/a1bec570-9455-9317-bf59-a787d5c072dc.png)

#### firstItemInSection

次に`firstItemInSection`です。

これはSectionの最初のセルをHeaderのように扱うように設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/c818c858-6113-e3c4-2d4b-891c3c0dd608.png)

この設定は
今回新しく登場した
階層構造を構築するようなレイアウトに使うことが
推奨されています。

ドキュメントにも下記のように書かれています。

```
Choose this header mode when you’re using hierarchical data sources 
to be able to expand and collapse the header.
```

この場合はセルの構築を行う中で
明示的に設定する必要があります。

```swift

dataSource = UICollectionViewDiffableDataSource<Int, Item>(collectionView: collectionView) {
    (collectionView: UICollectionView, indexPath: IndexPath, item: Item) -> UICollectionViewCell? in
    // ↓↓↓↓↓↓条件分岐が必要になる
    if indexPath.item == 0 {
        return collectionView.dequeueConfiguredReusableCell(using: headerRegistration, for: indexPath, item: item)
    } else {
        return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
    }
}
```

## List Cell

次にList Cellです。
iOS14では
`UICollectionViewListCell`
という`UICollectionViewListCell`のサブクラスが
新しく追加されました。

https://developer.apple.com/documentation/uikit/uicollectionviewlistcell

これはListスタイルのレイアウトを実現するために
下記の項目に関して
多くのデフォルト設定が用意されており
さらに独自にカスタマイズできるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/b44f46dd-6375-d551-c9be-7e204f68b4ad.png)

個々の項目について見ていきます。

### Separators

まずSeparatorsです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/7d388611-d31a-26e4-4b26-c4f8c2a22573.png)

これはよく見かけるようなレイアウトだと思いますが
例えばSeparatorをラベルの位置を合わせたいとします。
（ラベルとSeparatorのleadingを合わせたいとします。）

`UITableView`の場合
Separator insetを調整することができます。

しかし

- Safe area insets
- Dynamic font size
- SFSymbols

といったことを考慮し始めると
全ての場合において最適な状態にすることは難しくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/3d1c5579-feed-0b59-59b4-a507fa596ca2.png)

そこで
`UICollectionViewListCell`には
新しく**Separator Layout Guide**が登場しました。

https://developer.apple.com/documentation/uikit/uicollectionviewlistcell/3601206-separatorlayoutguide

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/8dfd4ed2-6435-9c8a-4ce0-ee252f40e92b.png)

このLayout Guideは
これまでのLayout Guideの設定方法とは異なり
Layout Guide自身に制約を設定して
Contentsに位置を合わせます。

ドキュメントによりますと

```
By default, when you apply a system-provided content configuration to a list cell, 
the separator automatically aligns to the primary text in the content view. 
For custom subviews in the cell, 
you need to add a constraint to this layout guide 
that connects it to the leading edge of the cell’s primary content.
```

フレームワーク側が提供している設定を利用している場合
デフォルトでprimary textにSeparatorに合わせるようになっているようです。

※
このprimaryの基準がどういうものなのか追えていないので
もしご存知の方がいらっしゃいましたら教えてください🙇‍♂️

### Swipe Action

次にSwipe　Actionです。

`UITableView`とは異なり
Swipe ActionはList Cellの一つの機能になっています。

そのため
セルの構築を行う際に一緒に設定をすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/ff45b680-3be2-dbac-b6f2-6e12b26e72c9.png)

ただし
この機能はレイアウトとセルの間でのやりとりが必要になるため
**list configurationを使用したセクションの中でのみ**動きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/426120ab-d038-391b-689b-93881fb98249.png)


独自のアクションを実装したい場合は
- leadingSwipeActionsConfiguration
- trailingSwipeActionsConfiguration

をOverrideすることで実現できます。

https://developer.apple.com/documentation/uikit/uicollectionviewlistcell/3601205-leadingswipeactionsconfiguration
https://developer.apple.com/documentation/uikit/uicollectionviewlistcell/3601207-trailingswipeactionsconfiguration

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/79658433-b006-c0cc-6571-482eea2f1443.png)

#### 注意点

気をつけなければならない点としては
**`IndexPath`を使用しない**ということです。

`IndexPath`はセルが挿入や削除されると位置がずれます。

仮に`IndexPath`でデータを管理している場合などは
間違ったデータの更新をしてしまう可能性があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/5bce2735-bad8-2648-35ad-c184892e5c19.png)


そこで`Identifier`を使用することで
そのリスクをなくすことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/016aa45e-040b-9bc2-bd40-2c5d25eb69ca.png)


※ 
本題とは関係ありませんが
`UITableViewRowAction`はiOS14でdeprecatedになりました。
https://developer.apple.com/documentation/uikit/uitableviewrowaction

#### 追記（2020/7/9）

Xcode12.0 Beta2で
`UICollectionLayoutListConfiguration`の
`leadingSwipeActionsConfigurationProvider`
`trailingSwipeActionsConfigurationProvider`
に移動したようです。

https://developer.apple.com/documentation/uikit/uicollectionlayoutlistconfiguration

サンプルでは
下記のように設定しています。

```swift

var configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
configuration.leadingSwipeActionsConfigurationProvider = { [weak self] (indexPath) in
    guard let self = self else { return nil }
    guard let item = self.dataSource.itemIdentifier(for: indexPath) else { return nil }
    return self.leadingSwipeActionConfigurationForListCellItem(item)
}
```

### Accessories

次にAccessoriesです。

`UITableView`では
- 扱える種類が少ない
- trailingにしか使えない

などの制約がありましたが
List Cellでは
かなり豊富な種類のAccessoriesを扱え
leading、trailing両方に設定できるようになりました。
さらにどちら側にも複数のAccessoriesを設定することが可能です。


また
UITableViewでは
Accessoriesは単なる装飾的な存在でしたが
List CellではAccessoriesを追加することで
Cellに機能を追加することができます。

https://developer.apple.com/documentation/uikit/uicellaccessory

例えば
このReorderを設定することで
ユーザがセルをタップをすると自動でCollectionViewがReorderモードになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/92f56c5a-472f-1965-99c1-1d7668bd5d37.png)

DeleteをタップするとtrailingにSwipe Actionが表示されたり
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/e844de66-4a0f-2259-671d-ccdb2c38f303.png)

Outline Disclosureはセクションのexpand collapseができるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/c43e7516-03ef-c70f-4bde-a9bfbee4abc4.png)

※
ただし現在のXcode12.0 Betaですと
サンプルコードでは
上記3つの中で
`disclosureIndicator`以外は
変更しても表示されませんでした。

設定方法はセルのaccessoriesというプロパティに設定します。

https://developer.apple.com/documentation/uikit/uicollectionviewlistcell/3600968-accessories

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/66fc32e6-d383-937a-25f8-3b9aea74ff9c.png)

表示方法ですが
デフォルトでは
UIKit側でどこに何を配置するのが最適かを
種類から判断し

自動で設定していると
セッションでは言っていました。

※
その基準がどうなっているのかまでは追えていないので
ご存知の方がいらっしゃいましたら教えてください🙇‍♂️

一方で様々に調整できるOptionも用意されいます。

例えば
下記ではEditモードではない場合にのみ
表示することができます。

https://developer.apple.com/documentation/uikit/uicellaccessory/displayedstate

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/d5262ae1-eb2a-352f-f8c6-9ff0c2e74a8e.png)

位置に関しては下記のようなenumがあるので変更できるようです。
https://developer.apple.com/documentation/uikit/uicellaccessory/placement

今回紹介した以外にも様々なAccessoriesが用意されています。

例えばメールアプリではチェックマークが使用されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/ca651ec8-5656-6d4f-fc15-cfe434d8b975.png)

## View Configuration

次にViewのConfigurationです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/4fdb2162-3fab-7f44-7fa6-ceeecd3ce025.png)


これまではセルに直接値の設定をしていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/4c0edf52-148a-0b87-73d9-ee3ea920d975.png)

これがConfigurationへ設定するように変わり
最後にcellの`contentConfiguration`へ適用します。

https://developer.apple.com/documentation/uikit/uicollectionviewcell/3600949-contentconfiguration

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/9ae16469-ddf5-6309-cdbc-71509f8fc427.png)

これは一見すると同じように見えますが
`contentConfiguration`に設定することで

特定の種類のセルだけではなく
**`contentConfiguration`に対応している全てのViewに**
同じ設定を適用できるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/d599c6a0-2f89-7b09-4579-5f3f91ef69b3.png)

※
ドキュメントで検索するとCellとHeaderFooterViewで利用できるようです。

<img width="384" alt="スクリーンショット 2020-06-30 8.02.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/79caff6e-cc47-ef8f-d53a-2ecd3460f94f.png">

`UIListConfiguration`はデフォルトのレイアウトが用意されているため
少ない設定でCollectionViewに標準的なレイアウトを適用することができます。

### ２つのConfiguration

これまではList Content Configurationを見てきましたが
Configurationには2つの種類があります。

- `UIBackgroundConfiguration`
- `UIListContentConfiguration`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/dfe3c042-a7c9-d630-3405-59ab7eea76eb.png)

### Background Configuration

Background Configurationには
セルのバックグラウンドに関連するプロパティの設定をします。

https://developer.apple.com/documentation/uikit/uibackgroundconfiguration

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/99465e0a-9205-860f-d54e-e4be7916cddc.png)

下記のように
`backgroundConfiguration`に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/8dc1f048-d982-495b-d6ce-0d055d57749e.png)

https://developer.apple.com/documentation/uikit/uicollectionviewcell/3600947-backgroundconfiguration

### List Content Configuration

そして
List Content Configurationは
`UIContentConfiguration`に適合した`UIListContentConfiguration`は
標準的な`UITableView`のセルやHeader、Footerのレイアウトを提供します。

`UIContentConfiguration`
https://developer.apple.com/documentation/uikit/uicontentconfiguration

また
個々のプロパティも設定できるため
カスタマイズも比較的自由にできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/765157cc-7b58-a0ef-2468-1779df7aed97.png)


上記でも見ましたが
下記が設定方法です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/fe1d3e4a-5c4c-2dda-0378-521620764c65.png)

### 推奨される使用方法

これらのConfigurationは
軽量でインスタンス生成のコストが非常に小さい
とセッション内では言っています。

また
Configurationはstructであり
cellに適用するまでは他に影響を与える心配がありません。

そこで推奨する使用方法としては
毎回`defaultConfiguration`メソッドから
新しいインスタンスを生成し
新しいconfigurationを設定していくのが良いと
セッション内では言っています。


こうすることで
古い状態に気にする必要もなくなりますし
複雑な状態や遷移処理が必要な場合にも
新しいconfigurationを毎回設定するだけになります。

さらに
Configurationはパフォーマンスにも良いと言っています。
これはUIKitと内部で連携することで
パフォーマンスの最適化が実現できていることによるようです。

## Configuration State

次にConfiguration Stateです。

これは
セルやViewのレイアウトを設定する際に
現在セルがどのような状態にあるのかをプロパティとして保持しています。

これは先ほど見た
Configurationの出力結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/b734d5a5-b1c5-1155-324a-18f7c4626d0a.png)

Configurationを利用することで
このような状態が異なる外見を
自動で利用できるようになります。

Configurationの実態は
特定の状態に応じた外見の記述です。

では
どういう状態があるのかを見ていきます。

### Traits

まず一番大きい単位として
下記のような`UITraitCollection`の値があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/45955068-0e5b-ba89-8878-58509e8132ad.png)

### States

次に
CellやViewがUIKitが内部に保持する状態があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/426fe4e9-d651-ef39-806a-565afca80e2f.png)


### Custom States

さらにその上に自身のカスタムな状態も実装することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/8de264c2-ddf0-0c94-ef09-81e42f10e48d.png)

### UIConfigurationState

これらの状態は
`UIConfigurationState`
を利用します。
https://developer.apple.com/documentation/uikit/uiconfigurationstate


具体的には
`UIViewConfigurationState`
`UICellConfigurationState`
を用いて保持します。

`UICollectionView`と`UITableView`のどちらにも適用できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/559c7767-032e-d766-88e9-e7aa28a48a9f.png)

#### `UIViewConfigurationState`

https://developer.apple.com/documentation/uikit/uiviewconfigurationstate

Trait Collectionの下に
下記のStateが存在します。

- Highlighted
- Selected
- Disabled
- Focused

さらにそこに自身のカスタムなStateを追加できます。
`UIConfigurationStateCustomKey`を使います。

ドキュメントに使用例があります。

```swift

// Declare a custom key for a custom isArchived state. 
extension UIConfigurationStateCustomKey {
    static let isArchived = UIConfigurationStateCustomKey("com.my-app.MyCell.isArchived")
}

// Declare an extension on the cell state structure to provide a typed property for this custom state.
extension UICellConfigurationState {
    var isArchived: Bool {
        get { return self[.isArchived] as? Bool ?? false } 
        set { self[.isArchived] = newValue }
    }
}

class MyCell: UICollectionViewCell {
    // This is an existing custom property of the cell.
    var isArchived: Bool { 
        didSet {
            // Ensure that an update is performed whenever this property changes.
            if oldValue != isArchived { 
                setNeedsUpdateConfiguration()
            }
        }
    }

    override var configurationState: UICellConfigurationState {
        // Get the structure from UIKit with the system properties set by calling super. 
        var state = super.configurationState

        // Set the custom property on the state.
        state.isArchived = self.isArchived 
        return state
    }

    override func updateConfiguration(using state: UICellConfigurationState) {
        // Get the default background configuration styling for the current state based on the system states.
        var backgroundConfig = UIBackgroundConfiguration.listPlainCell().updated(for: 
state)

        // Customize the background configuration based on the custom state. 
        if state.isArchived {
            backgroundConfig.visualEffect = UIBlurEffect(style: .systemMaterial) 
        }

        // Apply the background configuration to the cell.
        self.backgroundConfiguration = backgroundConfig 
    }
}
```

https://developer.apple.com/documentation/uikit/uiconfigurationstatecustomkey

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/1d9582a1-1048-12ed-a147-aa28659232c4.png)


#### `UICellConfigurationState`


`UICellConfigurationState`は
`UIViewConfigurationState`
にさらに

-  Editing
-  Swiped
-  Expanded
-  Drag and Drop

を追加した状態保持しています。

カスタムな状態の実装方法は同じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/48c9ff66-1bbd-1434-8f24-361b09966a43.png)

https://developer.apple.com/documentation/uikit/uicellconfigurationstate


### Configurationの更新

`updateConfiguration(using:)`を用います。
https://developer.apple.com/documentation/uikit/uicollectionviewcell/3600950-updateconfiguration

まず現在の状態に応じたConfigurationを取得し
そのconfigurationに値を設定していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/7d6446b3-6f28-651b-31e9-b89b30ee0be3.png)


このメソッドは直接呼ぶのではなく
`setNeedsUpdateConfiguration`を呼び出すことで間接的に呼び出すようにします。

https://developer.apple.com/documentation/uikit/uicollectionviewcell/3600432-setneedsupdateconfiguration


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/c64158fe-04a5-e80c-ac5a-d0bc05c65a95.png)

デフォルトでは
Configuration Stateが更新される度に
このメソッドが呼ばれ、
新しい状態に応じたConfigurationを自動で返します。


この動作を抑えるためには

`automaticallyUpdatesContentConfiguration`
`automaticallyUpdatesBackgroundConfiguration`
をfalseにします。

https://developer.apple.com/documentation/uikit/uicollectionviewcell/3600428-automaticallyupdatescontentconfi

https://developer.apple.com/documentation/uikit/uicollectionviewcell/3600427-automaticallyupdatesbackgroundco

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/287873eb-3939-509e-d10d-4f9174241e7e.png)


### 使用例

これまで紹介したものと同じように
最初のConfigurationを取得し
状態に応じて設定の変更を行い
最後にconfigurationをセルに設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/5d1c8e42-6c17-6987-4f75-e79da9a43881.png)

### `UIConfigurationColorTransformer`

上記の例でも
ステータスに応じてカラーを設定していましたが
`UIConfigurationColorTransformer`
を使ってよりカスタマイズしやすい形で設定することができます。

https://developer.apple.com/documentation/uikit/uiconfigurationcolortransformer

`UIConfigurationColorTransformer`はColorを引数に受け取り
別のColorに変換する一つのメソッドを持つstructです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/f52da335-bf85-c223-036b-e1578ee5a3a2.png)

これを用いることで
同じColorから様々な状態に応じたColorを作り出すことができます。


## Dynamic Layoutにも自動で対応

Dynamic Font Typeや
デバイスに応じたレイアウトにも自動で対応しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/1a473653-e941-1519-ebd0-f05519d5962e.png)


<img width="1152" alt="スクリーンショット 2020-06-30 17.50.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/c2f98314-d4f6-94bc-8bee-4b8ed678eb96.png">

## layout Margins

Content Configurationは
MarginやPaddingを調整することができます。

この値と実際に表示される値から
intrinsic heightが計算され
Self-Sizingされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/94021b43-75d0-5f50-13f9-1f4e917ed582.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/a260c5ad-67b2-5dcf-3299-cec2ab7092e7.png)


## Image reserved layout size

最後にデフォルトの設定から
調整が必要な例が挙げられていましたので紹介します。

下記のレイアウトは
それぞれ異なるイメージですが
幅が一緒のイメージが使用されているおり
レイアウトに問題はなさそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/73e0fb39-eb9a-1e3e-d280-f62fac2d0b59.png)

しかし
異なる幅の画像を使用すると
デフォルトの設定では
テキストは左揃えになりません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/b0a50c71-9ad2-fc93-6344-cf4cb118569e.png)

これはイメージがleadingに揃える設定になっていることと
イメージとテキストの間のPaddingが同じ値になるように設定されていることが原因です。

これをテキストが左揃えになるように調整するために
`reservedLayoutSize`を使用します。

https://developer.apple.com/documentation/uikit/uilistcontentconfiguration/imageproperties/3601000-reservedlayoutsize

すべてのセルに同じ幅を設定することによって
イメージが`reservedLayoutSize`に合わせて
水平に真ん中寄せになります。

イメージの大きさには影響はありません。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/d99e9a4f-0873-4d64-af2d-6caf63bd5140.png)

## (2020/8/23追記)textLayoutGuide

`UIListContentConfiguration`に`text`(= primary text)を設定している場合に
その`text`の位置を基準にしたlayoutGuideを取得できます。

https://developer.apple.com/documentation/uikit/uilistcontentview/3650226-textlayoutguide

Appleのサンプルでは
`separatorLayoutGuide`と合わせて使って
separatorをtextに合わせるようにしています。

```swift

private var separatorConstraint: NSLayoutConstraint?
private func updateSeparatorConstraint() {
    guard let textLayoutGuide = listContentView.textLayoutGuide else { return }
    if let existingConstraint = separatorConstraint, existingConstraint.isActive {
       return
    }
    let constraint = separatorLayoutGuide.leadingAnchor.constraint(equalTo: textLayoutGuide.leadingAnchor)
    constraint.isActive = true
    separatorConstraint = constraint
}
```

`secondaryText`には
`secondaryTextLayoutGuide`があります。
https://developer.apple.com/documentation/uikit/uilistcontentview/3650225-secondarytextlayoutguide

`image`には
`imageLayoutGuide`があります。
https://developer.apple.com/documentation/uikit/uilistcontentview/3650224-imagelayoutguide

## 既存の実装にConfigurationを追加時の注意点

いくつかConfigurationを使用する上での注意点が述べられていました。

#### Background configurationは項目をリセットする

Background configurationを使用すると
background colorやbackground viewはnilになるようです。

そのため既存の設定が反映されなくなる可能性があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/865323f5-7421-b11d-359b-0511e2996c0d.png)

#### Content configurationは値を上書きする。

Content configurationが保持しているプロパティで
- imageView
- textLabel
- detailedTextLabel

のようなセル、Header、Footerの
ビルトインのsub viewが保持している
プロパティは上書きされます。

※
内容と直接関係ありませんが
こういったプロパティを直接Viewに設定する既存の方法は
**将来的にdeprecatedになるようです。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/32073c7c-3a2c-f6b7-0d0f-144fcdaa53b0.png)

##　他のViewと一緒にListsを利用する

`UIListContentView`を利用することで
他のViewと一緒に使うことができます。
https://developer.apple.com/documentation/uikit/uilistcontentview

使い方としては
これまでと同様に
`UIListContentConfiguration`に値を設定して
`UIListContentView`に反映し
これをSubViewとして追加すれば可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/bd8925fb-53c3-b4bb-0cd9-88fb085a0bde.png)

`UIListContentConfiguration`が保持している
デフォルトのスタイルを利用しCustom Viewを簡単に作れるようになります。

`UIListContentView`は`UIView`なので
`CollectionView`や`TableView`以外でも
利用可能です。

### Custom Configurationを作成する 

自身でConfigurationを作成することもできます。

その際は
`UIContentConfiguration`に適合させたstructを定義します。
https://developer.apple.com/documentation/uikit/uicontentconfiguration

そしてViewには`UIContentView`プロトコルに適合したViewを作成し
`configuration`プロパティに設定します。
https://developer.apple.com/documentation/uikit/uicontentview
https://developer.apple.com/documentation/uikit/uicontentview/3600985-configuration

Configurationを利用することで
Viewが新しい状態に変わった時の自動アップデートなど
多くのメリットを活用することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/4fe12d23-9b78-6787-2e88-ccd368bc2d0f.png)

# 全体的なUICollectionViewの構築例

最後になりますが
セッション内で紹介されていた
`UICollectionView`の構築方法を見ていきます。

下記のようなレイアウトをリストを構築します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/1795eb3f-9a42-c296-6869-21bc8ddbd4d3.png)

## レイアウト

### Configurationの設定

まず
`UICollectionLayoutListConfiguration`を用いて
具体的なレイアウトの内容を設定します。

今回は`.sidebar`のスタイルを利用しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/78d2ef03-56fe-08b6-763d-01c981e3bdd1.png)

### `UICollectionViewCompositionalLayout`に設定

そして
`UICollectionViewCompositionalLayout`の`list`メソッドに
引数として先ほどの`UICollectionLayoutListConfiguration`を渡すことで
垂直にスクロールするレイアウトが作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/0da33458-3965-a803-4e5f-4fafae7aa5b4.png)


### `UICollectionView`に適用

最後に`UICollectionView`に
`UICollectionViewCompositionalLayout`を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/bb4673c3-cc4d-278c-6f9c-1ac0a1d60083.png)

## データの設定

次にデータの設定です。
セルの内容と自身のモデルを繋げます。

この例では
- `title`
- `image`
を保持した`MyItem`と呼ばれるモデルを利用しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/101bc042-b3bb-2aba-96d4-fb7a8f357ff8.png)

### セルの登録

まずは利用するセルとモデルを
`CellRegistration`を利用して登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/3830f591-61ac-37e2-b6ed-50160b94e3b0.png)

クロージャ内で
具体的にモデルをセルをどうやって設定していくのか
を定義します。

ここでは
`defaultContentConfiguration`メソッドで取得したインスタンスを
そのまま利用しています。

`title`と`image`をcontentに設定し
最後にセルに設定を反映させます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/f109f30f-526b-235e-df63-28ddf807a055.png)

### Data Sourceの生成

データとセルは
Data Sourceを経由して繋がります。

表示が必要になった際に
Data Sourceのクロージャで定義された方法で
セルを構築していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/b42f8862-7243-6ac6-9594-8f00d7bcb6e5.png)

今回は`UICollectionViewDiffableDataSource`を利用しています。

Section TypeとItem Typeを設定して
Diffable Data Sourceを生成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/ef1dc09e-0ab7-a293-0052-561f143e72c8.png)

Diffable Data Sourceは
具体的なCollectionViewを引数に受け取り初期化します。

セルを構築する際には
クロージャで
`collectionView`, `indexPath`, `item`を受け取って
構築していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/fd3e9877-466e-ffb3-b812-8779fd1140d2.png)


クロージャではCellを返します。
`dequeueConfiguredReusableCell`で
先ほど作成した`CellRegistration`を渡します。

これだけでセルの再利用などは
フレームワークが自動で管理してくれるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/d6f39e51-a827-0053-bd41-0f81cc8dfdf7.png)


## 結果

最終的に下記のようなリストが構築できます。
セルの状態に応じてbackground colorなども
自動で変更されるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/665e90eb-18e2-7a8b-0e8f-40f6c5035b9e.png)


appearanceに`.sidebarPlain`を設定すると
下記のようなレイアウトになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219965/1b8f43bd-1d27-70fd-9587-11d2a18cf5bf.png)

## サンプル

こちらのサンプルが非常にわかりやすくシンプルに実装されています。
https://github.com/kharrison/CodeExamples/tree/main/ListCollection

# まとめ

今年のアップデートを見ていきました。

昨年に引き続き
たくさんのアップデートがありました。

特に今年はUITableViewにとって変わるような
Listsが登場したことが大きく印象に残っています。

デフォルトで必要な設定は何もせずに利用できることで
コードを短くすることができる上に
カスタマイズができる余地も与えている点が
非常にバランスが取れているのではないかと感じています。

そしてUITableViewは
今後なくなっていくのではないかと思いました。
(セッションの中でも置き換えることを推奨しているような発言がありました)

もう一つ気になった点としては
Section Snapshotで
`SectionSnapshotHandlers`
`ReorderingHandlers`
といったstructが用いられていた点です。

これまでUIKitではdelegateパターンを用いて
ユーザのアクションなどを受け取る設計が多く見られましたが

今回のようなstructを利用することで
定義とハンドリング処理を同じ場所で
より宣言的にハンドリング処理を記載できるなど

さらに見やすくなったのではないかなと個人的には思いました。

より具体的にどういう風に利用するのかを
見たい方は公式のサンプルコードがおすすめです。

これを見れば
今年の更新内容や
昨年登場した`DiffableDataSource`、`CompositionalLayout`
の使い方も把握できると思います。

https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views

`UICollectionView`は
どんどん進化していっているので
今後もアップデートが楽しみです😀

間違いなどございましたらご指摘頂けますと嬉しいです🙇‍♂️