记录 RxDataSource 的简单使用过程, 特别是用在 TableView 的时候.

RxDataSource 的简单使用

用到的库: RxDataSources.

整个使用流程如下所示:

  1. 定义 Section 和 Cell 的数据结构:

    swift
    /// Section 模型
    struct AddNewAddressViewCellNormalSection {
        var header: String
        var items: [AddNewAddressViewCellType]
    }
    
    /// 扩展 Section 模型, 让其遵守 SectionModelType 协议, 以便数据源使用
    extension AddNewAddressViewCellNormalSection: SectionModelType {
        init(original: AddNewAddressViewCellNormalSection, items: [AddNewAddressViewCellType]) {
            self = original
            self.items = items
        }
    }
    
    /// 多种类型 Cell 数据模型定义
    enum AddNewAddressViewCellType {
        case textAndTextField(String, String, String, Bool)
        case textAndSwitch(String, Bool)
    }

    上面代码中定义了一个 TableView 中的 Section 模型, 这个模型必须要有的存储属性是 items, 其中的元素类型就是 Cell 的模型.

  2. 定义好数据结构后, 就可以组装数据了. 数据可以通过 Variable 来提供(当然也可以用 PublishSubject 等 Subject, 比如要网络获取数据的情况):

    swift
    let celldata = Variable<[AddNewAddressViewCellNormalSection]>([
            // section0
            AddNewAddressViewCellNormalSection(header: "", items:
                [.textAndTextField("收货人", "", "输入收件人姓名", false),
                 .textAndTextField("手机号码", "", "输入收件人手机号", false),
            // section1
            AddNewAddressViewCellNormalSection(header: "", items: [.textAndSwitch("设置为默认收货地址", false)])
            ])

    上面的数据中定义了两个 section, 且每个 section 中使用的都是不同的 cell 数据模型.

  3. 设置 TableView 的数据源和代理:

    swift
    // 绑定数据源
    viewModel.celldata.asObservable()
    	.bind(to: _tableView.rx.items(dataSource: getDataSource())).disposed(by: _disposeBag)
    
    // 设置代理
    _tableView.rx.setDelegate(self).disposed(by: _disposeBag)
    
    // 其中的 getDataSource() 方法如下所示:
    
    private func getDataSource() -> RxTableViewSectionedReloadDataSource<AddNewAddressViewCellNormalSection> {
        return RxTableViewSectionedReloadDataSource<AddNewAddressViewCellNormalSection>(
            configureCell: { dataSource, tableView, indexPath, item in
                switch item {
                case .textAndTextField(let labelText, let content, let placeHolder, let showClosure):
                    // dequeue cell 并根据 item 配置 cell
                case .textAndSwitch(let labelText, let isOn):
                    // dequeue cell 并根据 item 配置 cell
                }
        })
    }

通过上述方式设置后, 如果要更新数据, 只要在之前创建的 celldata (类型是 Subject 中的一种) 上更新最新数据, 则 TableView 便可以同步更新.

若 tableView 有其他的设置, 可以在 tableView 上直接进行, 也可以在代理中处理:

swift
_tableView.isScrollEnabled = false
_tableView.tableFooterView = UIView()
_tableView.backgroundColor = .clear
_tableView.rowHeight = 45
_tableView.register(AddNewAddressViewEditableCell.self,
                            forCellReuseIdentifier: AddNewAddressViewEditableCell.reuseID)
_tableView.register(AddNewAddressViewSwitchableCell.self,
                            forCellReuseIdentifier: AddNewAddressViewSwitchableCell.reuseID)

注意数据源中的类型是 Section 数组, 而非 Section 类型! 否则编译会报错.

如果要将 cell 上的一些控件点击都绑定到控制器对应的视图模型上, 则需要在配置数据源的时候进行处理.