Observer - 观察者
观察者 是用来监听事件,然后它需要这个事件做出响应。例如:弹出提示框就是观察者,它对点击按钮这个事件做出响应。
响应事件的都是观察者
那么我们还是用这几个例子来解释一下什么是观察者:
-
当室温高于 33 度时,打开空调降温
打开空调降温就是**观察者** `Observer<Double>`。
-
当《海贼王》更新一集时,我们就立即观看这一集
观看这一集就是**观察者** `Observer<OnePieceEpisode>`。
- 当取到 JSON 时,将它打印出来
将它打印出来就是**观察者** `Observer<JSON>`
-
当任务结束后,提示用户任务已完成
提示用户任务已完成就是**观察者** `Observer<Void>`
如何创建观察者
现在我们已经知道观察者主要是做什么的了。那么我们要怎么创建它们呢?
和 Observable一样,框架已经帮我们创建好了许多常用的观察者。例如:view
是否隐藏,button
是否可点击, label
的当前文本,imageView
的当前图片等等。
另外,有一些自定义的观察者是需要我们自己创建的。这里介绍一下创建观察者最基本的方法,例如,我们创建一个弹出提示框的的观察者:
tap.subscribe(onNext: { [weak self] in
self?.showAlert()
}, onError: { error in
print("发生错误: \(error.localizedDescription)")
}, onCompleted: {
print("任务完成")
})
创建观察者最直接的方法就是在 Observable
的 subscribe
方法后面描述,事件发生时,需要如何做出响应。而观察者就是由后面的 onNext
,onError
,onCompleted
的这些闭包构建出来的。
以上是创建观察者最常见的方法。当然你还可以通过其他的方式来创建观察者,可以了解一下 AnyObserver 和 Binder
特征观察者
和 Observable 一样,观察者也存特征观察者,例如:
- Binder
AnyObserver
AnyObserver 可以用来描叙任意一种观察者。
例如:
打印网络请求结果:
URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(onNext: { data in
print("Data Task Success with count: \(data.count)")
}, onError: { error in
print("Data Task Error: \(error)")
})
.disposed(by: disposeBag)
可以看作是:
let observer: AnyObserver<Data> = AnyObserver { (event) in
switch event {
case .next(let data):
print("Data Task Success with count: \(data.count)")
case .error(let error):
print("Data Task Error: \(error)")
default:
break
}
}
URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(observer)
.disposed(by: disposeBag)
用户名提示语是否隐藏:
usernameValid
.bind(to: usernameValidOutlet.rx.isHidden)
.disposed(by: disposeBag)
可以看作是:
let observer: AnyObserver<Bool> = AnyObserver { [weak self] (event) in
switch event {
case .next(let isHidden):
self?.usernameValidOutlet.isHidden = isHidden
default:
break
}
}
usernameValid
.bind(to: observer)
.disposed(by: disposeBag)
Binder
Binder 主要有以下两个特征:
- 不会处理错误事件
- 确保绑定都是在给定 Scheduler 上执行(默认 MainScheduler)
一旦产生错误事件,在调试环境下将执行 fatalError
,在发布环境下将打印错误信息。
示例
在介绍 AnyObserver时,我们举了这样一个例子:
let observer: AnyObserver<Bool> = AnyObserver { [weak self] (event) in
switch event {
case .next(let isHidden):
self?.usernameValidOutlet.isHidden = isHidden
default:
break
}
}
usernameValid
.bind(to: observer)
.disposed(by: disposeBag)
由于这个观察者是一个 UI 观察者,所以它在响应事件时,只会处理 next
事件,并且更新 UI 的操作需要在主线程上执行。
因此一个更好的方案就是使用 Binder:
let observer: Binder<Bool> = Binder(usernameValidOutlet) { (view, isHidden) in
view.isHidden = isHidden
}
usernameValid
.bind(to: observer)
.disposed(by: disposeBag)
Binder 可以只处理 next
事件,并且保证响应 next
事件的代码一定会在给定 Scheduler 上执行,这里采用默认的 MainScheduler。
复用
由于页面是否隐藏是一个常用的观察者,所以应该让所有的 UIView
都提供这种观察者:
extension Reactive where Base: UIView {
public var isHidden: Binder<Bool> {
return Binder(self.base) { view, hidden in
view.isHidden = hidden
}
}
}
usernameValid
.bind(to: usernameValidOutlet.rx.isHidden)
.disposed(by: disposeBag)
这样你不必为每个 UI 控件单独创建该观察者。这就是 usernameValidOutlet.rx.isHidden
的由来,许多 UI 观察者 都是这样创建的:
-
按钮是否可点击
button.rx.isEnabled
:extension Reactive where Base: UIControl { public var isEnabled: Binder<Bool> { return Binder(self.base) { control, value in control.isEnabled = value } } }
-
label
的当前文本label.rx.text
:extension Reactive where Base: UILabel { public var text: Binder<String?> { return Binder(self.base) { label, text in label.text = text } } }
你也可以用这种方式来创建自定义的 UI 观察者。
青山不改,绿水长流,后会有期,感谢大家支持!