★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(let_us_code)
➤博主域名:https://www.zengqiang.org
➤GitHub地址:https://github.com/strengthen/LeetCode
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
强烈推荐!!!试用博主个人App作品!提需求!提建议!
App Store搜索:【Mind Draft】
中国区可直接点击跳转:【Mind Draft】
三、两种编程方式的比较样例
1,效果图
这里我以最常见的 tableView 数据展示功能为例作为演示。后面分别使用传统写法,以及使用 RxSwift 响应式写法来实现,大家可以比较下它们的区别。
- 表格中显示的是歌曲信息(歌名,以及歌手)
- 点击选中任意一个单元格,在控制台中打印出对应的歌曲信息。
2,准备工作
首先我们创建一个 Music 的结构体,用来保存歌曲名称、歌手名字。此外它还遵循 CustomStringConvertible 协议,方便我们输出调试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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 所使用的数据源。
1
2
3
4
5
6
7
8
9
10
11
|
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 行代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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)”
1
2
3
4
5
6
7
8
9
10
11
|
import RxSwift //歌曲列表数据源 struct MusicListViewModel { let data = Observable .just([ Music (name: "无条件" , singer: "陈奕迅" ), Music (name: "你曾是少年" , singer: "S.H.E" ), Music (name: "从前的我" , singer: "陈洁仪" ), Music (name: "在木星" , singer: "朴树" ), ]) } |
- 这里我们不再需要实现数据源和委托协议了。而是写一些响应式代码,让它们将数据和 UITableView 建立绑定关系。
- 算了下这里我们只需要 31 行代码,同之前的相比,一下减少了 1/4 代码量。而且代码也更清爽了些。
代码的简单说明:
- DisposeBag:作用是 Rx 在视图控制器或者其持有者将要销毁的时候,自动释法掉绑定在它上面的资源。它是通过类似“订阅处置机制”方式实现(类似于 NotificationCenter 的 removeObserver)。
- rx.items(cellIdentifier:):这是 Rx 基于 cellForRowAt 数据源方法的一个封装。传统方式中我们还要有个 numberOfRowsInSection 方法,使用 Rx 后就不再需要了(Rx 已经帮我们完成了相关工作)。
- rx.modelSelected: 这是 Rx 基于 UITableView 委托回调方法 didSelectRowAt 的一个封装。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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) } } |