RxDataSources에서 여러 섹션에 여러 항목을 관리합니다.

10317 단어 RxDataSourcesRxSwift
RxDataSources 을 사용하여 여러 섹션에서 여러 항목을 관리하는 방법에 대한 비망록입니다.

RxDataSources 사용법



DataSource 정보



RxDataSources에 RxSwift의 RxCollectionViewDataSourceType RxTableViewDataSourceType 에 준거하고 있는 class가 준비되어 있다.
이제 데이터 소스를 만들고 rx.items(dataSource:)에 bind합니다.

  • UICollectionView의 경우
  • RxCollectionViewSectionedReloadDataSource
  • RxCollectionViewSectionedAnimatedDataSource


  • UITableView의 경우
  • RxTableViewSectionedReloadDataSource
  • RxTableViewSectionedAnimatedDataSource


  • SectionModelType 정보



    각 DataSource를 사용하려면, SectionModelType , 혹은 AnimatableSectionModelType 에 준거하고 있는 형태를 준비할 필요가 있습니다.
    이 중의 items 가 그 Section내의 셀 모델에 해당합니다.
    protocol SectionModelType {
        associatedtype Item
        var items: [Item] { get }
        init(original: Self, items: [Item])
    }
    

    ※AnimatableSectionModelType의 경우는, 자신이 IdentifiableType 에 준거하고 있고, Item이 IdentifiableType , Equatable 에 준거하고 있을 필요가 있습니다.
    protocol AnimatableSectionModelType: SectionModelType, IdentifiableType where Item: IdentifiableType, Item: Equatable {}
    
    protocol IdentifiableType {
        associatedtype Identity: Hashable
        var identity : Identity { get }
    }
    

    RxDataSources는 위의 각각을 준수하는 다음 유형을 제공합니다.
    struct SectionModel<Section, ItemType> {
        var model: Section
        var items: [Item]
    
        init(model: Section, items: [Item]) {
            self.model = model
            self.items = items
        }
    }
    
    struct AnimatableSectionModel<Section: IdentifiableType, ItemType: IdentifiableType & Equatable> {
        var model: Section
        var items: [Item]
        init(model: Section, items: [ItemType]) {
            self.model = model
            self.items = items
        }
    }
    

    구현해보기



    한 섹션



    준비되어 있는 AnimatableSectionModel<Section, ItemType> 를 사용해 구현해 봅니다.
    우선, IdentifiableTypeEquatable 에 준거하고 있는 형태를 준비합니다.
    struct Card: IdentifiableType, Equatable {
        typealias Identity = String
        var identity: String {
            return id
        }
        let id: String
        let color: UIColor
    }
    
    AnimatableSectionModel<String, Card> 로 취급하면 다음과 같은 형태가 됩니다.
    ※Section은, RxDataSources로 extension String : IdentifiableType 가 준비되어 있으므로 그것을 사용하고 있습니다.
    var dataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Card>> {
        return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Card>>(
            configureCell: { _, collectionView, indexPath, card -> UICollectionViewCell in
                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)!
                cell.backgroundColor = card.color
                return cell
        })
    }
    
    cards.asObservable()
        .map { cards: [Card] in
            [AnimatableSectionModel<String, Card>(model: "", items: cards)] 
        }
        .bind(to: collectionView.rx.items(dataSource: dataSource))
        .disposed(by: disposeBag)
    

    ↓추가와 셔플과 삭제를 하고 있습니다.


    애니메이션 있음 (섹션 1 개)





    여러 섹션(공통 셀 모델)



    Section이 복수 있는 경우는 AnimatableSectionModel<Section: IdentifiableType, ItemType: IdentifiableType & Equatable> 를 복수 건네주면 OK입니다.

    다만, 같은 identifier를 이용했을 경우, fatalError()가 되기 때문에 주의입니다.
    ※다른 Section, 다른 형태의 경우에서도 같은 identifier라면 안 된다
    var dataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Card>> {
        return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Card>>(
            configureCell: { model, collectionView, indexPath, card -> UICollectionViewCell in
                // Section毎にcellを返す
                let sectionModel = model.sectionModels[indexPath.section]
                switch sectionModel.model {
                case "1":
                    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)!
                    cell.backgroundColor = card.color
                    return cell
                case "2":
                    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)!
                    cell.backgroundColor = card.color
                    return cell
                default:
                    return UICollectionViewCell()
                }
        })
    }
    cards.asObservable()
        .map { cards: [Card] in
            // 雑ですが同じ値を使います。idだけ重複しないよう`2_`を先頭に加えています。
            let copy = cards.map { Card(id: "2_\($0.id)", color: $0.color) }
            return [AnimatableSectionModel<String, Card>(model: "1", items: cards),
                    AnimatableSectionModel<String, Card>(model: "2", items: copy)]
        }
        .bind(to: collectionView.rx.items(dataSource: dataSource))
        .disposed(by: disposeBag)
    


    애니메이션 있음 (섹션 2 개)





    여러 섹션(여러 셀 모델)



    다른 셀 모델의 경우 형식으로 래핑되어 캐스팅 할 준비가되어야합니다. (IdentifiableType, Equatable에 준거하고 있다)
    이번에는 enum을 준비해 봅니다.
    struct Item01: IdentifiableType, Equatable {
        typealias Identity = String
        var identity: Identity {
            return id
        }
        let id: String
        let color: UIColor
    }
    
    struct Item02: IdentifiableType, Equatable {
        typealias Identity = String
        var identity: Identity {
            return id
        }
        let id: String
        let color: UIColor
    }
    
    struct Item03: IdentifiableType, Equatable {
        typealias Identity = String
        var identity: Identity {
            return id
        }
        let id: String
        let color: UIColor
    }
    enum Item: IdentifiableType, Equatable {
        typealias Identity = String
        var identity: Identity {
            switch self {
            case .item01(let item):
                return item.identity
            case .item02(let item):
                return item.identity
            case .item03(let item):
                return item.identity
            }
        }
        case item01(Item01)
        case item02(Item02)
        case item03(Item03)
    }
    

    그리고는, 똑같이 RxCollectionViewSectionedAnimatedDataSource 를 구현합니다.
    셀을 생성하는 장소는 Section(String)이나 Item에서 기호에 분기로 OK입니다.
    var dataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Item>> {
        return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Item>>(
            configureCell: { model, collectionView, indexPath, item -> UICollectionViewCell in
                let sectionModel = model.sectionModels[indexPath.section]
                switch sectionModel.model {
                case "0":
                    switch item {
                    case let .item01(i):
                        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)
                        cell.backgroundColor = i.color
                        return cell
                    case let .item02(i):
                        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)
                        cell.backgroundColor = i.color
                        return cell
                    case let .item03(i):
                        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)
                        cell.backgroundColor = i.color
                        return cell
                    }
                case "1":
                    switch item {
                    case let .item01(i):
                        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)
                        cell.backgroundColor = i.color
                        return cell
                    case let .item02(i):
                        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)
                        cell.backgroundColor = i.color
                        return cell
                    case let .item03(i):
                        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "任意のidentifier", for: indexPath)
                        cell.backgroundColor = i.color
                        return cell
                    }
                default:
                    return UICollectionViewCell()
                }
        })
    }
    Observable
        .combineLatest(item01.asObservable(),
                       item02.asObservable())
        .map { (item01: [Item], item02: [Item]) in
            return [AnimatableSectionModel<String, Item>(model: "0", items: item01),
                    AnimatableSectionModel<String, Item>(model: "1", items: item02)]
        }
        .bind(to: collectionView.rx.items(dataSource: delegate.dataSource))
        .disposed(by: disposeBag)
    


    애니메이션 있음 (섹션 2 개로 여러 셀 모델)





    이상입니다!
    그리고는, String으로 하고 있는 Section를 형태로 한, cell의 생성 부분을 정리하면 좋다고 생각합니다.

    ※덤(tvOS)


    애니메이션 있음 (섹션 2 개로 여러 셀 모델)


    좋은 웹페이지 즐겨찾기