RxSwift learning: RxCocoa Basics (PART 1)

UITableView

Table for a single partition

var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView = UITableView(frame: view.bounds, style: Register (uitableViewCell. self, forCellReuseIdentifier: Let items = Observable. Just (["Swift", "objective-c ", "JavaScript", "Python", "Go"]) // Set tableView data (cellForRow encapsulation) kitems.bind (to: tableView.rx.items) { tableView, row, element in let cell = tableView.dequeueReusableCell(withIdentifier: "cellid")! cell.textLabel? .text = "\(row): \(element)" return cell }.disposed(by: disposeBag) }Copy the code

The cell selects the event response

// Set tableView data (which encapsulates cellForRow) kitems.bind (to: tableView.rx.items) { tableView, row, element in let cell = tableView.dequeueReusableCell(withIdentifier: "cellid")! cell.textLabel? .text = "\(row): \(element)" return cell }.disposed(by: DisposeBag) / / for the selected item index tableView. The rx. ItemSelected. Subscribe (onNext: {indexPath in print (" indexPath of selected items are as follows: \ [indexPath) ")}) disposed (by: disposeBag) / / get the content of the selected item tableView. Rx. ModelSelected (String. The self). The subscribe (onNext: Prompt {item in print(" Item title: \(item)")}). Disposed (by: DisposeBag) / / access index and contents of the selected item observables. Zip (tableView. Rx. ItemSelected, TableView. Rx. ModelSelected (String. Self)). The bind {[weak self] indexPath, item in print (" ~ indexPath of selected items are as follows: \(indexPath)") print("~ item ")}. Disposed (by: disposeBag)Copy the code

Cell uncheck the event response

/ / get cancelled the index of the selected item tableView. Rx. ItemDeselected. Subscribe (onNext: {[weak self] indexPath in print (" indexPath to uncheck items: \ [indexPath) ")}) disposed (by: disposeBag) / / get cancelled the content of the selected item tableView. Rx. ModelDeselected (String. The self). The subscribe (onNext: {item in print(" Deselect item title disposed(item)")}). Disposed (by: DisposeBag) / / access index and contents of the selected item observables. Zip (tableView. Rx. ItemSelected, TableView. Rx. ModelDeselected (String. Self)). The bind {[weak self] indexPath, item in print (" ~ indexPath to uncheck items: Disposed (by: disposeBag) \(indexPath)") print("~ deschecked item title disposed(by: disposeBag)Copy the code

Cell deletion event response

/ / open cell editing tableView. SetEditing (true, animated: true) / / get deleted items index tableView. Rx. ItemDeleted. Subscribe (onNext: {indexPath in print(" Delete item indexPath: \(indexPath)")}). Disposed (by: DisposeBag) tableView. Rx. ModelDeleted (String. The self). The subscribe (onNext: {item in print (" delete item item: \(item)") }).disposed(by: DisposeBag) / / delete item index and the content of observables. Zip (tableView. Rx. ItemDeleted, TableView. Rx. ModelDeleted (String. Self)). The bind {indexPath, item in print (" to delete the item indexPath: \(indexPath)") print("~ deleted item: \(item)")}. Disposed (by: disposeBag)Copy the code

Cell movement event response

/ mobile/cell incident response tableView. SetEditing (true, animated: true) tableView. Rx. ItemMoved. Subscribe (onNext: {sourceIndexPath, destinationIndexPath in print(" move item original indexPath: \(sourceIndexPath)") print(" move item now indexPath: \(sourceIndexPath)") print(" move item now indexPath: \ [destinationIndexPath) ")}) disposed (by: disposeBag) / / for insert item index tableView. Rx. ItemInserted. Subscribe (onNext: {[weak self] indexPath in print(" Insert item indexPath is: \(indexPath)")}). Disposed (by: DisposeBag) / / access to click on the icon to the end of the index tableView. Rx. ItemAccessoryButtonTapped. Subscribe (onNext: {[weak self] indexPath in print(" Tail item indexPath is: \(indexPath)")}). Disposed (by: disposeBag)Copy the code

Cell insert event response

/ / events that will display cell response tableView. Rx. WillDisplayCell. Subscribe (onNext: {cell, indexPath in print(" To display indexPath is: \(indexPath)") print(" to display cell is: \(indexPath)")}). Disposed (by: disposeBag)Copy the code

Set the agent

Set agent TableView.rx.setdelegate (self).disposed(by: disposeBag)Copy the code

Implementing proxy methods

// MARK: - UITableViewDelegate extension RxDataSourcesWithHeaderController: UITableViewDelegate {// Set cell height func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {return 60} func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {return 40} UITableView, viewForHeaderInSection section: Int) -> UIView? { let headerView = UIView() /// ... return headerView } }Copy the code

UITableView + RxDataSources

RxDataSources introduction

RxDataSources Github address: RxDataSources

Official introduction:

UITableView and UICollectionView Data Sources for RxSwift (sections, animated updates, editing ...)
Copy the code

Features:

  1. The essence of RxDataSource is to use RxSwift to wrap the data source of UITableView and UICollectionView. Using it can greatly reduce our workload

  2. RxDataSources uses sections as data structures. So regardless of whether our tableView is single-partitioned or multi-partitioned, when we use RxDataSources, we need to return an array of sections

The installation

  • CocoaPods

Podfile

Pod 'RxDataSources',' ~ > 3.0 'Copy the code
  • Carthage

Cartfile

Making "RxSwiftCommunity/RxDataSources" ~ > 3.0Copy the code

Single partition TableView

Method 1: Use built-in sections

// Create table tableView = UITableView(frame: view.bounds, style: Register (uitableViewCell. self, forCellReuseIdentifier: AddSubview (tableView) // Initialize data let items = Observable. Just ([SectionModel(model: "", items: ["UILabel usage ", "UIButton usage ", "UITextField at the usage of the"]]) / / create the data source dataSource = RxTableViewSectionedReloadDataSource < SectionModel < String, String>>(configureCell: { (dataSource, tableView, indexPath, element) -> UITableViewCell in let cell = tableView.dequeueReusableCell(withIdentifier: "cellID")! cell.textLabel? .text = "\(indexPath. Row): \(element)" return cell}) // Bind (to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)Copy the code

Method 2: Use custom sections

Custom SectionModel

struct XMSessionModel {
    var header: String
    var items: [Item]
}

extension XMSessionModel: AnimatableSectionModelType {
    
    typealias Item = String
    
    var identity: String {
        return header
    }
    
    init(original: XMSessionModel, items: [Item]) {
        self = original
        self.items = items
    }
}
Copy the code
// Create table tableView = UITableView(frame: view.bounds, style: Register (uitableViewCell. self, forCellReuseIdentifier: Let sections = Observable.just([XMSessionModel(header: "", items: ["UILabel usage ", "UIButton usage ", "UITextField at the usage of the"]]) / / create the data source dataSource = RxTableViewSectionedReloadDataSource < XMSessionModel > (configureCell: { (dataSource, tableView, indexPath, item) -> UITableViewCell in let cell = tableView.dequeueReusableCell(withIdentifier: "cellID") ?? UITableViewCell(style: .default, reuseIdentifier: "cellID") cell.textLabel?.text = "\(indexPath.row): Item "return cell}) // Data binding section.bind (to: tableview.rx.items (dataSource: dataSource)).disposed(by: disposeBag)Copy the code

Multi-partitioned TableView

Method 1: Use built-in sections

// Create table tableView = UITableView(frame: view.bounds, style: Register (uitableViewCell. self, forCellReuseIdentifier: "CellID ") view.addSubView (tableView) // Initialize data let items = Observable. Just ([SectionModel(model:" basic control ", items: ["UILabel usage ", "UIButton usage ", "UITextField usage "], SectionModel(model: "advanced I control ", items: [" Use of UITableView ", "UICollectionView usage"]]) / / create the data source dataSource = RxTableViewSectionedReloadDataSource < SectionModel < String, String>>( configureCell: { (dataSource, tableView, indexPath, element) -> UITableViewCell in let cell = tableView.dequeueReusableCell(withIdentifier: "cellID")! cell.textLabel? .text = "\(indexPath.row): \ (element) "return cell}) / / set the head title dataSource. TitleForHeaderInSection = {the dataSource, Index return in the dataSource. SectionModels [index] model} / tail/set partition title dataSource. TitleForFooterInSection = {the dataSource, Item in return "footer"} // Data binding items.bind(to: tableview.rx.items (dataSource: dataSource)).disposed(by: disposeBag)Copy the code

Method 2: Use custom sections

XMSessionModel refer to the above structure

// Create table tableView = UITableView(frame: view.bounds, style: Register (uitableViewCell. self, forCellReuseIdentifier: Let items = Observable. Just ([XMSessionModel(header: "basic control ", items:)) view.addSubView (tableView) ["UILabel ", "UIButton ", "UITextField "]), XMSessionModel(header: "advanced I control ", items: [" Use of UITableView ", "UICollectionView usage"]]) / / create the data source dataSource = RxTableViewSectionedReloadDataSource < XMSessionModel > ( configureCell: { (dataSource, tableView, indexPath, element) -> UITableViewCell in let cell = tableView.dequeueReusableCell(withIdentifier: "cellID")! cell.textLabel? .text = "\(indexPath.row): \ (element) "return cell}) / / set the head title dataSource. TitleForHeaderInSection = {the dataSource, Index return in the dataSource. SectionModels [index]. Header} / tail/set partition title dataSource. TitleForFooterInSection = {the dataSource, Item in return "footer"} // Data binding items.bind(to: tableview.rx.items (dataSource: dataSource)).disposed(by: disposeBag)Copy the code

UITableView + Refresh

TableView is usually used in conjunction with Refresh functionality, which mostly takes data from the network and refreshes it

The data request and table refresh are simulated below

Data fetch, return list data

Func getRandomResult() -> Observable<[SectionModel<String, Int>]> {let items = (0.. <5).map { _ in Int(arc4random()) } let observable = Observable.just([SectionModel(model: "S", items: }) return Observable. Delay (2, scheduler: MainScheduler.Copy the code

Create tables and bind data

/ / / set the navigation bar on the right side button to refresh the let refreshButton = UIBarButtonItem () refreshButton. Title = "refresh" self. NavigationItem. RightBarButtonItem RefreshButton tableView = UITableView(frame: view.bounds, style: Register (uitableViewCell. self, forCellReuseIdentifier: "CellID") the addSubview (tableView) / / initialized data let randomResult = refreshButton. Rx. Tap. AsObservable (). The throttle (1, scheduler: Mainscheduler.instance) // If multiple changes occur within 1 second, FlatMapLatest (getRandomResult) // Only fetch the last data. share(replay: 1) / / create the data source dataSource = RxTableViewSectionedReloadDataSource < SectionModel < String, Int > > (configureCell: { (dataSource, tableView, indexPath, element) -> UITableViewCell in let cell = tableView.dequeueReusableCell(withIdentifier: "cellID")! cell.textLabel? .text = "item \(indexPath. Row): \(element)" return cell}) // Data binding randomresult. bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)Copy the code

Stopping data requests

Use takeUntil to stop the data request

Let cancelButton = UIBarButtonItem () cancelButton. Title = "cancel" self. NavigationItem. LeftBarButtonItem = cancelButton ` ` ` / / initialized data let randomResult = refreshButton, rx. Tap. AsObservable (). The throttle (1, the scheduler: Mainscheduler.instance) // If multiple changes occur within 1 second, The last time.startwith (()) is added to automatically request data at the beginning. FlatMapLatest ({// core code Self.getrandomresult ().takeuntil (self.cancelbutton.rx.tap)}) // Stop data request.share (replay: 1)Copy the code

Since the | address

Swift Books download:Download address