iOS14のUICollectionViewを必要最小限のコードで実装する。

# きっかけ

WWDC2020でUICollectionViewに新APIが追加され、いろんな方がその概要や細かい機能を書いてくれていてとてもありがたい！
のですが、僕の理解不足か具体的にどう書けばいいのか・コードの全体像がわからず...
とても苦労をしたので、他にも同じ苦労している人がいるのでは！と思い記事にしました。

## 実装イメージ

<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/738919/95e025e5-96e3-4a49-2ea5-62d9e021671b.png">

## 全コード

```swift:ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    
    enum Section: CaseIterable {
        case kanto
        case kyusyu
    }
    
    let data: [Section: [String]] = [
        .kanto: ["Kanto", "Tokyo", "Chiba"],
        .kyusyu: ["Kyusyu", "Fukuoka", "Miyazaki"]
    ]

    var collectionViewDataSource: UICollectionViewDiffableDataSource<Section, String>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        var layoutConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
        layoutConfig.headerMode = UICollectionLayoutListConfiguration.HeaderMode.firstItemInSection

        let layout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
        collectionView.collectionViewLayout = layout
        
        collectionViewDataSource = createDataSource()
        reloadList()
    }
    

    func createDataSource() -> UICollectionViewDiffableDataSource<Section, String> {
        let normalCell = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, indexPath, text) in
            var content = cell.defaultContentConfiguration()
            content.text = text
            cell.contentConfiguration = content
        }
        
        return UICollectionViewDiffableDataSource<Section, String>(collectionView: collectionView) {
            (collectionView, indexPath, text) -> UICollectionViewCell? in
        
            return collectionView.dequeueConfiguredReusableCell(using: normalCell, for: indexPath, item: text)
        }
    }
    
    func reloadList(){
        var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
        snapshot.appendSections(Section.allCases)
        snapshot.appendItems(data[.kanto]!, toSection: .kanto)
        snapshot.appendItems(data[.kyusyu]!, toSection: .kyusyu)
        collectionViewDataSource.apply(snapshot)
    }
}
```

## かいせつ

### 表示データ定義
```swift
enum Section: CaseIterable {
    case kanto
    case kyusyu
}

let data: [Section: [String]] = [
    .kanto: ["Kanto", "Tokyo", "Chiba"],
    .kyusyu: ["Kyusyu", "Fukuoka", "Miyazaki"]
]
```

ここでは、表示するデータを定義しています。
`data`一番最初の「Kanto」と「Kyusyu」はセクションタイトルです。
（実装イメージを見ての通りセクションは自動で大文字になるみたいです。）

### レイアウト設定

```swift
var layoutConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
layoutConfig.headerMode = UICollectionLayoutListConfiguration.HeaderMode.firstItemInSection

let layout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
collectionView.collectionViewLayout = layout
```

ここでは、collectionViewのレイアウトを設定しています。
今回はデフォルトで使用できるレイアウトを使っていますが、セルの大きさなどカスタマイズするときはここら辺を変更する予定です。
`HeaderMode.firstItemInSection`←ここがデータの最初をセクションタイトルとして扱う設定です。

### セル設定

```swift
func createDataSource() -> UICollectionViewDiffableDataSource<Section, String> {
	let normalCell = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, indexPath, text) in
		var content = cell.defaultContentConfiguration()
		content.text = text
		cell.contentConfiguration = content
	}
	
	return UICollectionViewDiffableDataSource<Section, String>(collectionView: collectionView) {
		(collectionView, indexPath, text) -> UICollectionViewCell? in
	
		return collectionView.dequeueConfiguredReusableCell(using: normalCell, for: indexPath, item: text)
	}
}
```

ここでは、渡されたデータをどのように表示するかを設定しています。
従来の`UICollectionViewDataSource`の
`func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell`
と同じ役割です。

`normalCell`は、collectionViewにセルを登録するための変数です。登録とセルの設定が一度にできるので見通しがよくなりました。

### データ更新
```swift
func reloadList(){
	var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
	snapshot.appendSections(Section.allCases)
	snapshot.appendItems(data[.kanto]!, toSection: .kanto)
	snapshot.appendItems(data[.kyusyu]!, toSection: .kyusyu)
	collectionViewDataSource.apply(snapshot)
}
```

最初に定義したデータをcollectionViewに`apply`することによって画面に表示しています。

## まとめ

セクションタイトルをもっといい感じに書きたい気持ちはありますが、今までと比べてすっきり書くことができました。
この記事では基本的なUICollectionViewの機能しか使っていないので、他の記事でもっと応用的な記事も書いていこうと思います。

励みになるのでよかったらLGTMお願いします＼(^o^)／