一、iOS的MVVM
下图是MVVM-C
设计模式的结构图,其中的C
指的不是控制器,而是作为展示或者关闭控制器的Coordinate
(协调器)。在开发中,我们一般在Controller
中完成展示或者关闭控制器
的任务,所以这里我们不关注协调器。
1. 职责划分
相比MVC
来说,新增了一个VM
, 下面是各个模块的职责:
VM:VM
是V
和M
之间的桥梁, 提供一系列属性用于View
的显示,属性包含将Model
变形转换为View
展示时应有的值。在iOS中通常还会负责网络请求及Model
更新。
VC:负责建立VM
中属性与View
的绑定关系;负责交互事件响应的具体逻辑;如果不建立图中协调器时,通常还包括还包括页面的跳转逻辑。
V: 视图的具体创建和用户交互监听,模型中数据的呈现逻辑。
M:负责存储和管理应用程序所需的数据,以及执行相关的业务逻辑。它不应该与V
或者VM
或者控制器
产生耦合。
2. 响应式编程RxSwift
来做绑定
- 上述提到
VC
中负责建立绑定关系,我们可以使用KVO
来实现,但不推荐;RxSwift
是专门用于响应式编程的一套框架,提供了很多变形相关的函数。 - 使用
RxSwift
,可以根据需要来实现单向或者双向的绑定,当我们熟练RxSwift
的函数后,能提升我们的代码质量和便捷性。
二、一个响应式编程例子
这里是使用RxSwift的简单介绍,如果不感兴趣可以跳过。
var modelObject: ModelObject!
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
modelObject.valueObservable.map { possibleValue -> String in
if let value = possibleValue {
return "Selected value is: \(value)"
} else {
return "No value selected"
}
}.bind(to: self.textLabel.rx.text).disposed(by: disposeBag)
}
1. 为什么绑定很重要?
如上代码,相比于在很多地方设置 textLabel.text
的值,现在这个 textLabel
只会在最后被引用一次。响应式编程让我们从目的地---也就是数据的订阅者开始,一路通过数据变形进行回溯,直到到达原始的数据依赖 - 可观察量 (observable
)。通过这么做,数据管道的可观察量
,数据变形
以及订阅者
三者得以分离。
数据变形的部分是响应式编程所能带来的最大优势,但同时它也是学习曲线最为陡峭的部分。
2. RxSwift中的一些类型
-
Observable
是一系列值的串流,我们可以对它们进行变形,订阅,或者将它们绑定到UI 元素上去。 -
PublishSubject
是Observable
的一种,我们可以将值发送给它,这些值会被发给观察者。 -
BehaviorSubject
、ReplaySubject
和PublishSubject
类似,不过我们可以在没有任何观察者连接上它时就进行值的发送,新的观察者会接收到暂存在 “重放” 缓冲区上的之前被发送的值。 -
Disposable
和DisposeBag
分别用来控制一个或多个订阅的生命周期。当一个Disposable
被销毁或者手动丢弃时,订阅行为就将结束,另外该订阅的所有的可观察量组成部分也将被释放。
3. RxSwift 中的部分变形函数
-
Map
:映射 -
Filter
:过滤 -
concat
:将两个A、B两个Observable“串行”起来,在A发送onComplete前只会接受A的消息,A发送onComplete后才会接收B的消息。 -
Merge
: 合并多个可观察序列,当其中一个发出消息时,会收到订阅回调。 -
take
与take(while
、take(until
等:控制订阅次数或根据触发条件订阅。 -
flatMapLatest
:只保留flatMapLatest
返回的最新一个Observable
的订阅(flatMapLatest
函数返回的是一个Observable
)。
一个学习RxSwift的网站:https://www.hangge.com/blog/cache/category_72_1.html
三、一个双向绑定的例子
大多情况下,我们只需要单向绑定;但有时候可能会需要双向绑定,下面是一个双向绑定的示例:
登录页输入框要与VM的数据双向绑定
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var label: UILabel!
var userVM = UserViewModel()
let disposeBag = DisposeBag()
override func viewDidLoad() {
//将用户名与textField做双向绑定
userVM.username.asObservable().bind(to: textField.rx.text).disposed(by: disposeBag)
textField.rx.text.orEmpty.bind(to: userVM.username).disposed(by: disposeBag)
//将用户信息绑定到label上
userVM.userinfo.bind(to: label.rx.text).disposed(by: disposeBag)
}
}
// 我们可以将双向绑定定义一个为一个操作符(官方demo中有这个文件,可拷贝)
// 上述中双向绑定的代码可以简化为:
//将用户名与textField做双向绑定
_ = self.textField.rx.textInput <-> self.userVM.username
如果感兴趣的话,可以在RxSwift
中的github
上的样板工程:
https://github.com/ReactiveX/RxSwift