在详细介绍 RxSwift
相关的知识点之前,我想先通过一个样例演示下 RxSwift
到底能做什么,好让大家能够对其有一个直观的了解。
三、两种编程方式的比较样例
1,效果图
这里我以最常见的 tableView
数据展示功能为例作为演示。后面分别使用传统写法,以及使用 RxSwift
响应式写法来实现,大家可以比较下它们的区别。
- 表格中显示的是歌曲信息(歌名,以及歌手)
- 点击选中任意一个单元格,在控制台中打印出对应的歌曲信息。
2,准备工作
首先我们创建一个 Music
的结构体,用来保存歌曲名称、歌手名字。此外它还遵循 CustomStringConvertible
协议,方便我们输出调试。
import UIKit
//歌曲结构体
struct Music {
let name: String //歌名
let singer: String //演唱者
init(name: String, singer: String) {
self.name = name
self.singer = singer
}
}
//实现 CustomStringConvertible 协议,方便输出调试
extension Music: CustomStringConvertible {
var description: String {
return "name:\(name) singer:\(singer)"
}
}
3,过去我们会这么做(传统式编程)
(1)首先写一个 ViewModel
- 这里面没有什么太复杂的东西,就是生成一个
UITableView
所使用的数据源。
import Foundation
//歌曲列表数据源
struct MusicListViewModel {
let data = [
Music(name: "无条件", singer: "陈奕迅"),
Music(name: "你曾是少年", singer: "S.H.E"),
Music(name: "从前的我", singer: "陈洁仪"),
Music(name: "在木星", singer: "朴树"),
]
}
(2)视图控制器代码(ViewController.swift)
- 接着我们设置
UITableView
的委托,并让视图控制器实现UITableViewDataSource
和UITableViewDelegate
协议,及相关的协议方法。 - 这个大家肯定都写过无数遍了,也没什么好讲的。算一下,这里一共需要 43 行代码。
import UIKit
import RxSwift
class ViewController: UIViewController {
//tableView对象
@IBOutlet weak var tableView: UITableView!
//歌曲列表数据源
let musicListViewModel = MusicListViewModel()
override func viewDidLoad() {
super.viewDidLoad()
//设置代理
tableView.dataSource = self
tableView.delegate = self
}
}
extension ViewController: UITableViewDataSource {
//返回单元格数量
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return musicListViewModel.data.count
}
//返回对应的单元格
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell")!
let music = musicListViewModel.data[indexPath.row]
cell.textLabel?.text = music.name
cell.detailTextLabel?.text = music.singer
return cell
}
}
extension ViewController: UITableViewDelegate {
//单元格点击
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("你选中的歌曲信息【\(musicListViewModel.data[indexPath.row])】")
}
}
4,现在使用 RxSwift 进行改造(响应式编程)
(1)对 ViewModel
做些修改
- 这里我们将
data
属性变成一个可观察序列对象(Observable Squence)
,而对象当中的内容和我们之前在数组当中所包含的内容是完全一样的。 - 关于可观察序列对象在后面的文章中我会详细介绍。简单说就是“序列”可以对这些数值进行“订阅
(Subscribe)
”,有点类似于“通知(NotificationCenter)
”
import RxSwift
//歌曲列表数据源
struct MusicListViewModel {
let data = Observable.just([
Music(name: "无条件", singer: "陈奕迅"),
Music(name: "你曾是少年", singer: "S.H.E"),
Music(name: "从前的我", singer: "陈洁仪"),
Music(name: "在木星", singer: "朴树"),
])
}
(2)视图控制器代码(ViewController.swift)
- 这里我们不再需要实现数据源和委托协议了。而是写一些响应式代码,让它们将数据和 UITableView 建立绑定关系。
- 算了下这里我们只需要 31 行代码,同之前的相比,一下减少了 1/4 代码量。而且代码也更清爽了些。
代码的简单说明:
DisposeBag
:作用是Rx
在视图控制器或者其持有者将要销毁的时候,自动释法掉绑定在它上面的资源。它是通过类似“订阅处置机制”方式实现(类似于NotificationCenter
的removeObserver
)。rx.items(cellIdentifier:)
:这是Rx
基于cellForRowAt
数据源方法的一个封装。传统方式中我们还要有个numberOfRowsInSection
方法,使用Rx
后就不再需要了(Rx
已经帮我们完成了相关工作)。rx.modelSelected
: 这是Rx
基于UITableView
委托回调方法didSelectRowAt
的一个封装。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
//tableView对象
@IBOutlet weak var tableView: UITableView!
//歌曲列表数据源
let musicListViewModel = MusicListViewModel()
//负责对象销毁
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//将数据源数据绑定到tableView上
musicListViewModel.data
.bind(to: tableView.rx.items(cellIdentifier:"musicCell")) { _, music, cell in
cell.textLabel?.text = music.name
cell.detailTextLabel?.text = music.singer
}.disposed(by: disposeBag)
//tableView点击响应
tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
print("你选中的歌曲信息【\(music)】")
}).disposed(by: disposeBag)
}
}