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