目录
前言
一、环境准备
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。