RxSwift 学习 - UITableView的操作

1,749 阅读2分钟

使用RxSwift开发tableView

Demo地址

  1. 确定数据类型 以及绑定cell

这里数据类型都需要通过 SectionModel 来包装 一个SectionModel 对象对应一个分区的数据。configCell这个闭包是用来配置cell的,每次数据模型变动都会重新刷新tableView。 这里数据类型一定要包装成sectionModel类型。不然会有问题

    lazy var dataSource : RxTableViewSectionedReloadDataSource<SectionModel<String,MenuModel>> = {
        let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,MenuModel>>(configureCell: { source, tableView, indexPath, item in
            let cell = tableView.dequeueReusableCell(withIdentifier: "MenuCell", for: indexPath) as! MenuCell
            cell.model = item
            cell.focusSubject.subscribe { (event) in
                guard let ele = event.element else { return }
                let sectionModels = self.dataSource.sectionModels.first
                guard let index = self.tableView.indexPath(for: cell) else { return }
                if index.row == 0 && index.section == 0 { return }
                self.tableView.moveRow(at: index, to: IndexPath.init(row: 0, section: 1))
                }.disposed(by: self.bag)
            return cell
        },canMoveRowAtIndexPath:{ (dataSource,indexPath) -> Bool in
            return true
        })
        return dataSource
    }()
    
    /*
    初始化dataSource对象,保存configureCell
    */
    public init(
                configureCell: @escaping ConfigureCell,
                titleForHeaderInSection: @escaping  TitleForHeaderInSection = { _, _ in nil },
                titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil },
                canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false },
                canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false },
                sectionIndexTitles: @escaping SectionIndexTitles = { _ in nil },
                sectionForSectionIndexTitle: @escaping SectionForSectionIndexTitle = { _, _, index in index }
            ) {
            self.configureCell = configureCell
            self.titleForHeaderInSection = titleForHeaderInSection
            self.titleForFooterInSection = titleForFooterInSection
            self.canEditRowAtIndexPath = canEditRowAtIndexPath
            self.canMoveRowAtIndexPath = canMoveRowAtIndexPath
            self.sectionIndexTitles = sectionIndexTitles
            self.sectionForSectionIndexTitle = sectionForSectionIndexTitle
        }
    
    /*
    在UITableViewDataSource中得到configureCell的返回值
    */    
    open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
    
        return configureCell(self, tableView, indexPath, self[indexPath])
    }
    
  1. 绑定viewModel 的数据源

监听viewModel.menuModels的变化和tableView的数据源进行绑定。当网络请求完成的时候,就会触发tableView的reloadData

    viewModel
            .menuModels
            .asObservable()
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by:bag)
            
            /*
            tableView.rx.items(dataSource: dataSource 
            */
            public func items<DataSource: RxTableViewDataSourceType & UITableViewDataSource,
            O: ObservableType>(dataSource: DataSource) -> (_ source: O) -> Disposable
                where DataSource.Element == O.E {
                return { source in
            _ = self.delegate
            // dataSource 开始订阅数据代码
            return source.subscribeProxyDataSource(ofObject: self.base, dataSource: dataSource as UITableViewDataSource, retainDataSource: true) { [weak tableView = self.base] (_: RxTableViewDataSourceProxy, event) -> Void in
                guard let tableView = tableView else {
                    return
                }
                dataSource.tableView(tableView, observedEvent: event)
            }
            
            func subscribeProxyDataSource<DelegateProxy: DelegateProxyType>(ofObject object: DelegateProxy.ParentObject, dataSource: DelegateProxy.Delegate, retainDataSource: Bool, binding: @escaping (DelegateProxy, Event<E>) -> Void)
                -> Disposable
                where DelegateProxy.ParentObject: UIView {
                let proxy = DelegateProxy.proxy(for: object)
                let unregisterDelegate = DelegateProxy.installForwardDelegate(dataSource, retainDelegate: retainDataSource, onProxyForObject: object)
                // this is needed to flush any delayed old state (https://github.com/RxSwiftCommunity/RxDataSources/pull/75)
                object.layoutIfNeeded()

                let subscription = self.asObservable()
                    .observeOn(MainScheduler())
                    .catchError { error in
                        bindingError(error)
                        return Observable.empty()
                    }
                    // source can never end, otherwise it would release the subscriber, and deallocate the data source
                    .concat(Observable.never())
                    .takeUntil(object.rx.deallocated)
                    .subscribe { [weak object] (event: Event<E>) in

                        if let object = object {
                            assert(proxy === DelegateProxy.currentDelegate(for: object), "Proxy changed from the time it was first set.\nOriginal: \(proxy)\nExisting: \(String(describing: DelegateProxy.currentDelegate(for: object)))")
                        }
                        
                        binding(proxy, event)
                        
                        switch event {
                        case .error(let error):
                            bindingError(error)
                            unregisterDelegate.dispose()
                        case .completed:
                            unregisterDelegate.dispose()
                        default:
                            break
                        }
                    }
                    
                return Disposables.create { [weak object] in
                    subscription.dispose()
                    object?.layoutIfNeeded()
                    unregisterDelegate.dispose()
                }
            }
            
            
            
            /*
            bind(to:) 调取之后返回的是
            */
            
            public func bind<R>(to binder: (Self) -> R) -> R {
                // 将数据源和tableView 之间进行绑定
                return binder(self)
            }
        }
    }
            
            
  1. tableView各种事件的监听和配置

tableView的sectionTitle,cellSelect之类都可以通过绑定的方法来监听。

tableView.rx.itemSelected.subscribe { event in
            guard let index = event.element else { return }
            let sectionModel = self.dataSource.sectionModels[index.section]
            let model = sectionModel.items[index.row]
            print("选中了 " + model.name)
            }.disposed(by: bag)