目錄
前言
一、環境準備
1.安裝 RxSwift 和 RxCocoa
2.導入模塊
二、實現一個簡單的UITableView
1.實現一個簡單的 UITableView
1.實現步驟
1.我們聲明一個ViewModel
2.ViewModel和UITableView 綁定
2.實現 UITableView 的代理方法
三、處理點擊事件
前言
????????在 iOS 開發中,UITableView 是我們最常使用的組件之一。傳統的寫法需要設置 delegate、datasource,實現多個方法。而使用 RxSwift + RxCocoa,我們可以讓列表渲染邏輯更清晰、響應式,更符合 MVVM 模式。
????????這篇文章將手把手教你用 RxSwift 來綁定 UITableView,構建一個“設置”頁面,包括多個 section 和點擊事件。
一、環境準備
1.安裝 RxSwift 和 RxCocoa
????????首先我們要安裝 RxSwift 和 RxCocoa。你可以選擇你喜歡的方式,我這里以 cocoapods 為例:
? ? ? ? 這里我使用的Xcode 的版本為Xcode 16.3,RxSwift 和 RxCocoa的版本號為 6.9.0。
pod 'RxSwift'
pod 'RxCocoa'
2.導入模塊
import RxSwift
import RxCocoa
二、實現一個簡單的UITableView
? ? ? ? 我們以一個簡單的 UITableView 為例,看看如何實現一個最簡單的 UITableView。具體要實現的 UI 效果如圖所示:
圖 1.簡單的 UITableView
1.實現一個簡單的 UITableView
1.實現步驟
????????我們看一下詳細的實現步驟:
1.我們聲明一個ViewModel
class SimpleTableViewModel {
? ? // 模擬數據源
? ? let items = Observable.just([
? ? ? ? "第一行", "第二行", "第三行", "第四行", "第五行"
? ? ])
}
2.ViewModel和UITableView 綁定
? ? ? ? 設置好數據之后,我們調用 bind 方法把 UITableView 和 ViewModel 綁定即可實現一個簡單的 UITableView。
? ? private func setupBindingUITableView(){
? ? ? ? tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
? ? ? ? tableView.dataSource = nil
? ? ? ? tableView.delegate = nil
? ? ? ? // 綁定 ViewModel 數據到 tableView
? ? ? ? viewModel.items
? ? ? ? ? ? .bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in
? ? ? ? ? ? ? ? cell.textLabel?.text = "\(index + 1).\(model)"
? ? ? ? ? ? ? ? cell.accessoryType = .disclosureIndicator
? ? ? ? ? ? }
? ? ? ? ? ? .disposed(by: disposeBag)
? ? }
? ? ? ? 完整的代碼如下:
import UIKit
import RxSwift
import RxCocoaclass SimpleTableViewModel {// 模擬數據源let items = Observable.just(["第一行", "第二行", "第三行", "第四行", "第五行"])
}class SimpleTableViewController: UITableViewController {private let disposeBag = DisposeBag()private let viewModel = SimpleTableViewModel()override func viewDidLoad() {super.viewDidLoad()self.title = "Rx 簡單列表"setupBindingUITableView()}private func setupBindingUITableView(){tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")tableView.dataSource = niltableView.delegate = nil// 綁定 ViewModel 數據到 tableViewviewModel.items.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell incell.textLabel?.text = "\(index + 1).\(model)"cell.accessoryType = .disclosureIndicator}.disposed(by: disposeBag)}
}
2.實現 UITableView 的代理方法
? ? ? ? RxCocoa 還封裝了UITableView的DataSource方法,例如我們想要實現 cell的點擊方法,僅需要調用下面的代碼即可:
? ? ? ? tableView.rx.itemSelected.subscribe {[weak self] indexPath in
? ? ? ? ? ? debugPrint("點擊了 Section: \(indexPath.section), Row: \(indexPath.row)")
? ? ? ? }.disposed(by: disposeBag)
? ? ? ? 除此之外,封裝了 itemDeselected(反選)、itemHighlighted(高亮)、itemUnhighlighted(取消高亮)、itemAccessoryButtonTapped(itemAccessory 點擊)、itemInserted(item 增加)、itemDeleted(item 刪除)等方法,這里您可以調用下看看。
? ? ? ? 這里我寫了一個簡單的 UITableView 數據源的增加和刪除功能 demo,完整代碼如下:
import UIKit
import SnapKit
import RxSwift
import RxCocoaclass RxSwiftTableViewModel {// 模擬數據源var items = BehaviorSubject(value: ["第一行", "第二行", "第三行", "第四行", "第五行"])// 增加一行數據func addItem() {var currentItems = try! items.value()currentItems.append("新的一行")items.onNext(currentItems)}// 刪除一行數據func removeItem(at index: Int) {var currentItems = try! items.value()if index >= 0 && index < currentItems.count {currentItems.remove(at: index)items.onNext(currentItems)}}}class RxSwiftUITableViewDemosVC: UIViewController {private let disposeBag = DisposeBag()private let viewModel = RxSwiftTableViewModel()private let tableView = UITableView()private let addButton = UIButton(type: .system)private let removeButton = UIButton(type: .system)override func viewDidLoad() {super.viewDidLoad()self.title = "RxSwift UITableView Demo"view.backgroundColor = .white// 使用 SnapKit 設置 tableView 布局setupTableView()// 使用 SnapKit 設置按鈕布局setupButtons()// 綁定 ViewModel 數據到 tableViewviewModel.items.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell incell.textLabel?.text = model}.disposed(by: disposeBag)// 處理點擊事件tableView.rx.itemSelected.subscribe(onNext: { [weak self] indexPath inself?.tableView.deselectRow(at: indexPath, animated: true)print("點擊了 Section: \(indexPath.section), Row: \(indexPath.row)")}).disposed(by: disposeBag)// 增加數據按鈕點擊事件addButton.rx.tap.subscribe(onNext: { [weak self] inself?.viewModel.addItem()}).disposed(by: disposeBag)// 刪除數據按鈕點擊事件removeButton.rx.tap.subscribe(onNext: { [weak self] in// 刪除最后一行數據let currentCount = (try? self?.viewModel.items.value().count ?? 0) ?? 0let lastIndex = currentCount - 1if lastIndex >= 0 {self?.viewModel.removeItem(at: lastIndex)}}).disposed(by: disposeBag)//選中 modeltableView.rx.modelSelected(String.self).subscribe { element indebugPrint("you selected section:\(element)")}.disposed(by: disposeBag)}private func setupTableView() {// 添加 tableView 到視圖view.addSubview(tableView)// 使用 SnapKit 設置 tableView 布局tableView.snp.makeConstraints { make inmake.top.equalToSuperview()make.left.right.equalToSuperview()make.bottom.equalToSuperview().offset(-100) // 留出按鈕位置}// 注冊 UITableViewCelltableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")}private func setupButtons() {// 添加按鈕到視圖view.addSubview(addButton)view.addSubview(removeButton)// 設置按鈕標題addButton.setTitle("增加一行數據", for: .normal)removeButton.setTitle("刪除一行數據", for: .normal)// 使用 SnapKit 設置按鈕布局addButton.snp.makeConstraints { make inmake.left.equalToSuperview().inset(20)make.bottom.equalToSuperview().inset(20)make.height.equalTo(44)make.width.equalToSuperview().dividedBy(2).offset(-30)}removeButton.snp.makeConstraints { make inmake.right.equalToSuperview().inset(20)make.bottom.equalToSuperview().inset(20)make.height.equalTo(44)make.width.equalToSuperview().dividedBy(2).offset(-30)}}
}
三、處理點擊事件
? ? ? ? 當你需要實現更復雜的 UITableView,可以實用 RxSwift+RxDataSource 實現更復雜的 UITableView。