The RxSwift is very powerful, well used and enjoyable

Routine: tableView refresh, is a data source, how to a callback.

For example, two tables are associated. RxDataSource, how to refresh the list, automatically select the first one. The sublist then confirms the data to refresh based on the selection of the previous list.

The problem is that RxDataSource focuses on the data processing of the list view, and automatically selecting the first row is something the tableViewDelegate does.

There’s a tableView on the left, and there’s a tableView on the right, and there’s a correlation between the two lists, and the left list is a level 1 option, and the right list is a level 2 option corresponding to the left.

Here’s what you can do:

private var lastIndex : NSInteger = 0 // ... // This is not the caselet leftDataSource = RxTableViewSectionedReloadDataSource<CategoryLeftSection>( configureCell: { [weak self] ds, tv, ip, item in
            guard  let strongSelf = self else { return UITableViewCell()}
            let cell : CategoryLeftCell = tv.dequeueReusableCell(withIdentifier: "Cell1".for: ip) as! CategoryLeftCell cell.model = item CategoryLeftCell cell.model = itemif ip.row == strongSelf.lastIndex {
                   // ...
                    tv.selectRow(at: ip, animated: false, scrollPosition: .top) tv.delegate?.tableView! (tv, didSelectRowAt: ip) }returncell }) vmOutput! .sections.asDriver().drive(self.leftMenuTableView.rx.items(dataSource: leftDataSource)).disposed(by: Rx.disposebag) // Select the list on the left and give the data provided on the rightlet rightPieceListData = self.leftMenuTableView.rx.itemSelected.distinctUntilChanged().flatMapLatest {
        [weak self](indexPath) ->  Observable<[SubItems]> in
            guard let strongSelf = self else { return Observable.just([]) }
           
            strongSelf.currentIndex = indexPath.row
            if indexPath.row == strongSelf.viewModel.vmDatas.value.count - 1 {
                // ...
                strongSelf.leftMenuTableView.selectRow(at: strongSelf.currentSelectIndexPath, animated: false, scrollPosition: .top) strongSelf.leftMenuTableView.delegate?.tableView! (strongSelf.leftMenuTableView, didSelectRowAt: strongSelf.currentSelectIndexPath!)return Observable.just((strongSelf.currentListData)!)
            }
            if let subItems = strongSelf.viewModel.vmDatas.value[indexPath.row].subnav {
                // ...
                var reult:[SubItems] = subItems
                // ...
                strongSelf.currentListData = reult
                strongSelf.currentSelectIndexPath = indexPath
                return Observable.just(reult)
            }
            returnObservable. Just ([])}.share(replay: 1) // The data source of the list on the rightlet rightListDataSource =  RxTableViewSectionedReloadDataSource<CategoryRightSection>( configureCell: { [weak self]ds, tv, ip, item in
            guard let strongSelf = self else { return UITableViewCell() }
            ifstrongSelf.lastIndex ! = strongSelf.currentIndex { tv.scrollToRow(at: ip, at: .top, animated:false)
                strongSelf.lastIndex = strongSelf.currentIndex
            }
            if ip.row == 0 {
                let cell :CategoryListBannerCell = CategoryListBannerCell()
                cell.model = item
                return cell
            } else {
                let cell : CategoryListSectionCell = tv.dequeueReusableCell(withIdentifier: "Cell2".for: ip) as! CategoryListSectionCell
                cell.model = item
                returnCell}}) / / set the right list agent rightListTableView. Rx. SetDelegate (self) disposed (by: Rx.disposebag) // RightpiecelistData. map{[CategoryRightSection(items:$0)] }.bind(to: self.rightListTableView.rx.items(dataSource: rightListDataSource))
            .disposed(by: rx.disposeBag)
Copy the code

If ip.row == strongself. lastIndex {, when updating data to the specified cell, operate.

Programs can run, but not elegantly.

Object oriented = object oriented = object oriented = object oriented

open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
           let cell : CategoryLeftCell = tv.dequeueReusableCell(withIdentifier: "Cell1".for: ip) as! CategoryLeftCell cell.model = item CategoryLeftCell cell.model = itemif ip.row == strongSelf.lastIndex {
                   // ...
                    tv.selectRow(at: ip, animated: false, scrollPosition: .top) tv.delegate?.tableView! (tv, didSelectRowAt: ip) // ... }return cell
    }
Copy the code

It’s a little bit clearer when you expand it like that. We’re not going to use it like this, if we don’t use Rx, just OOP. We’re going to use it like this and then we’re going to refresh and then we’re going to select it

tableView.reloadData()
tv.selectRow(at: ip, animated: false, scrollPosition: .top)

Copy the code

// Other operations

open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell : CategoryLeftCell = tv.dequeueReusableCell(withIdentifier: "Cell1".for: ip) as! CategoryLeftCell
            cell.model = item
            return cell
    }
Copy the code

Take a look at the source code

The leftDataSource specifies the data structure of each tableView section via the generic, and its only argument is a closure, configureCell.

let rightListDataSource =  RxTableViewSectionedReloadDataSource<CategoryLeftSection>( configureCell: { [weak self] ds, tv, ip, item in
// ...
}

Copy the code

Open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

The source code of the RxDataSource is quite clear, and you start with the precondition row to ensure that you don’t cross the boundary. The configureCell anonymous function is then called. This design is borrowed from Swift where functions are first class citizens and can be passed like values.

open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
        
        return configureCell(self, tableView, indexPath, self[indexPath])
    }
Copy the code

Better call timing:

Inheritance TableViewSectionedDataSource, create a subclass of they want, any change method based on the requirements.

Make an inheritance

final class MyDataSource<S: SectionModelType>: RxTableViewSectionedReloadDataSource<S> {
    private let relay = PublishRelay<Void>()
    var rxRealoded: Signal<Void> {
        return relay.asSignal()
    }
    
    override func tableView(_ tableView: UITableView, observedEvent: Event<[S]>) {
        super.tableView(tableView, observedEvent: observedEvent)
        // Do diff
        // Notify update
        relay.accept(())
    }
    
}

Copy the code

Because in this scenario, super.tableView(tableView, observedEvent: observedEvent), after you call it, you need to accept the event and send it out later. Therefore, use a PublishSubject. PublishRelay encapsulates the PublishSubject. The difference is that it is not as powerful as the PublishSubject. Completed and error are suitable for more specialized scenarios.

SharedSequence is a shared event stream. It’s the encapsulation of an Observable. Features such as call in the main thread, more suitable for doing UI.

Simple optimizations make code semantics more explicit

// left menu data sourcelet leftDataSource = MyDataSource<CategoryLeftSection>( configureCell: { ds, tv, ip, item in
            let cell : CategoryLeftCell = tv.dequeueReusableCell(withIdentifier: "Cell1".for: ip) as! CategoryLeftCell
            cell.model = item
            returnThe cell})/finished/refresh, do a choice leftDataSource. RxRealoded. Emit (onNext: {[weak self]in
            guard let self = self else { return }
            let indexPath = IndexPath(row: 0, section: 0)
            self.leftMenuTableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableView.ScrollPosition.none) self.leftMenuTableView.delegate?.tableView?(self.leftMenuTableView, didSelectRowAt: indexPath) }).disposed(by: rx.disposeBag) // ... // Leave the rest unchangedCopy the code

The semantics of the code

FRP is just writing out what you want to do, you don’t have to do OOP, you don’t have to do OOP, you don’t have to call it, you change the state.

Declarative programming changes in only one place. Even if you change that, declarative programming is called in only one place. OOP is harder to find.

Git repo, I put it on coding.net, pod is installed, download directly run