【RxSwift】UICollectionViewのbindについて

## はじめに
RxSwiftでCollectionViewを使用する際のバインド方法がいくつかあることを知り、簡単にまとめてみました。

## 開発環境
Xcode 13.3
Swift 5.6
RxSwift 6.5.0

## UICollectionViewのbindについて
### バインドパターン1
`items.bind(to: collectionView.rx.items)`でデータをバインド
indexPathを`IndexPath(row: row, section: 0)`としてセルを生成して返す

```swift
    let items = Observable.just([1, 2, 3])
         
    items.bind(to: collectionView.rx.items) { (collectionView, row, element) in
        let indexPath = IndexPath(row: row, section: 0)
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell
        cell.value?.text = "\(element) @ \(row)"
        return cell
    }.disposed(by: disposeBag)
```

### バインドパターン2
`items.bind(to: collectionView.rx.items(cellIdentifier: "Cell", cellType: NumberCell.self))`でCellを指定しながらデータをバインド

```swift
    let items = Observable.just([1, 2, 3])
         
    items.bind(to: collectionView.rx.items(cellIdentifier: "Cell", cellType: NumberCell.self)) { (row, element, cell) in
        cell.value?.text = "\(element) @ \(row)"
    }.disposed(by: disposeBag)
```

### バインドパターン3
変換を実行するために使用されるカスタムデータを使用して、`items.bind(to: collectionView.rx.items(dataSource: dataSource))`でバインド

```swift
    let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel<String, Double>>()

    let items = Observable.just([
             SectionModel(model: "First section", items: [1.0, 2.0, 3.0]),
             SectionModel(model: "Second section", items: [1.0, 2.0, 3.0]),
             SectionModel(model: "Third section", items: [1.0, 2.0, 3.0])])

    dataSource.configureCell = { (dataSource, cv, indexPath, element) in
        let cell = cv.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell
        cell.value?.text = "\(element) @ row \(indexPath.row)"
        return cell
    }

    items
        .bind(to: collectionView.rx.items(dataSource: dataSource))
        .disposed(by: disposeBag)
```

### セル選択時の処理

`collectionView(_:didSelectItemAtIndexPath:)`と同じ

```swift
    collectionView.rx.itemSelected
        .subscribe(onNext: { [unowned self] indexPath in
            self.collectionView.deselectItem(at: indexPath, animated: true)
            // ここに追加
        })
        .disposed(by: disposeBag)
```

### delegateの設定

通常のデリゲートとリアクティブデリゲートを併用できるようになる

##### `UICollectionViewDelegate`,`UICollectionViewDelegateFlowLayout`

```swift: UIScrollView+Rx
    public func setDelegate(_ delegate: UIScrollViewDelegate) -> Disposable {
        return RxScrollViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
    }
```

##### `UICollectionViewDataSource`

```swift: UICollectionView+Rx
    public func setDataSource(_ dataSource: UICollectionViewDataSource) -> Disposable {
        RxCollectionViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self.base)
    }
```


## おわりに
整理してみると大したことではなさそうなので、基本的には1番分かりやすい（`collectionView(_:cellForItemAt:)`に近い）パターン1で実装していけば良いかと思いました。

## 参照

https://qiita.com/hironytic/items/71bc729abe73ab9f0879

https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/iOS/UICollectionView%2BRx.swift

https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/iOS/UITableView%2BRx.swift

https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/iOS/UIScrollView%2BRx.swift