Swift - RxSwift的使用详解19(特征序列3:ControlProperty、 ControlEvent)
作者:hangge | 2018-02-09 08:10
五、ControlProperty
1,基本介绍
(1)ControlProperty 是专门用来描述 UI 控件属性,拥有该类型的属性都是被观察者(Observable)。
(2)ControlProperty 具有以下特征:
- 不会产生 error 事件
- 一定在 MainScheduler 订阅(主线程订阅)
- 一定在 MainScheduler 监听(主线程监听)
- 共享状态变化
2,使用样例
(1)其实在 RxCocoa 下许多 UI 控件属性都是被观察者(可观察序列)。比如我们查看源码(UITextField+Rx.swift),可以发现 UITextField 的 rx.text 属性类型便是 ControlProperty<String?>:
import RxSwift
import UIKit
extension Reactive where Base: UITextField {
public var text: ControlProperty<String?> {
return value
}
public var value: ControlProperty<String?> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { textField in
textField.text
},
setter: { textField, value in
if textField.text != value {
textField.text = value
}
}
)
}
//......
}
(2)那么我们如果想让一个 textField 里输入内容实时地显示在另一个 label 上,即前者作为被观察者,后者作为观察者。可以这么写:
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var label: UILabel!
let disposeBag = DisposeBag()
override func viewDidLoad() {
//将textField输入的文字绑定到label上
textField.rx.text
.bind(to: label.rx.text)
.disposed(by: disposeBag)
}
}
extension UILabel {
public var fontSize: Binder<CGFloat> {
return Binder(self) { label, fontSize in
label.font = UIFont.systemFont(ofSize: fontSize)
}
}
}
(3)运行结果如下:

六 、ControlEvent
1,基本介绍
(1)ControlEvent 是专门用于描述 UI 所产生的事件,拥有该类型的属性都是被观察者(Observable)。
(2)ControlEvent 和 ControlProperty 一样,都具有以下特征:
- 不会产生 error 事件
- 一定在 MainScheduler 订阅(主线程订阅)
- 一定在 MainScheduler 监听(主线程监听)
- 共享状态变化
2,使用样例
(1)同样地,在 RxCocoa 下许多 UI 控件的事件方法都是被观察者(可观察序列)。比如我们查看源码(UIButton+Rx.swift),可以发现 UIButton 的 rx.tap 方法类型便是 ControlEvent<Void>:
import RxSwift
import UIKit
extension Reactive where Base: UIButton {
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside)
}
}
(2)那么我们如果想实现当一个 button 被点击时,在控制台输出一段文字。即前者作为被观察者,后者作为观察者。可以这么写:
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
@IBOutlet weak var button: UIButton!
override func viewDidLoad() {
//订阅按钮点击事件
button.rx.tap
.subscribe(onNext: {
print("欢迎访问hangge.com")
}).disposed(by: disposeBag)
}
}
(3)运行结果如下:

附:给 UIViewController 添加 RxSwift 扩展
1,UIViewController+Rx.swift
这里我们对 UIViewController 进行扩展:- 将 viewDidLoad、viewDidAppear、viewDidLayoutSubviews 等各种 ViewController 生命周期的方法转成 ControlEvent 方便在 RxSwift 项目中使用。
- 增加 isVisible 序列属性,方便对视图的显示状态进行订阅。
- 增加 isDismissing 序列属性,方便对视图的释放进行订阅。
import UIKit
import RxCocoa
import RxSwift
public extension Reactive where Base: UIViewController {
public var viewDidLoad: ControlEvent<Void> {
let source = self.methodInvoked(#selector(Base.viewDidLoad)).map { _ in }
return ControlEvent(events: source)
}
public var viewWillAppear: ControlEvent<Bool> {
let source = self.methodInvoked(#selector(Base.viewWillAppear))
.map { $0.first as? Bool ?? false }
return ControlEvent(events: source)
}
public var viewDidAppear: ControlEvent<Bool> {
let source = self.methodInvoked(#selector(Base.viewDidAppear))
.map { $0.first as? Bool ?? false }
return ControlEvent(events: source)
}
public var viewWillDisappear: ControlEvent<Bool> {
let source = self.methodInvoked(#selector(Base.viewWillDisappear))
.map { $0.first as? Bool ?? false }
return ControlEvent(events: source)
}
public var viewDidDisappear: ControlEvent<Bool> {
let source = self.methodInvoked(#selector(Base.viewDidDisappear))
.map { $0.first as? Bool ?? false }
return ControlEvent(events: source)
}
public var viewWillLayoutSubviews: ControlEvent<Void> {
let source = self.methodInvoked(#selector(Base.viewWillLayoutSubviews))
.map { _ in }
return ControlEvent(events: source)
}
public var viewDidLayoutSubviews: ControlEvent<Void> {
let source = self.methodInvoked(#selector(Base.viewDidLayoutSubviews))
.map { _ in }
return ControlEvent(events: source)
}
public var willMoveToParentViewController: ControlEvent<UIViewController?> {
let source = self.methodInvoked(#selector(Base.willMove))
.map { $0.first as? UIViewController }
return ControlEvent(events: source)
}
public var didMoveToParentViewController: ControlEvent<UIViewController?> {
let source = self.methodInvoked(#selector(Base.didMove))
.map { $0.first as? UIViewController }
return ControlEvent(events: source)
}
public var didReceiveMemoryWarning: ControlEvent<Void> {
let source = self.methodInvoked(#selector(Base.didReceiveMemoryWarning))
.map { _ in }
return ControlEvent(events: source)
}
//表示视图是否显示的可观察序列,当VC显示状态改变时会触发
public var isVisible: Observable<Bool> {
let viewDidAppearObservable = self.base.rx.viewDidAppear.map { _ in true }
let viewWillDisappearObservable = self.base.rx.viewWillDisappear
.map { _ in false }
return Observable<Bool>.merge(viewDidAppearObservable,
viewWillDisappearObservable)
}
//表示页面被释放的可观察序列,当VC被dismiss时会触发
public var isDismissing: ControlEvent<Bool> {
let source = self.sentMessage(#selector(Base.dismiss))
.map { $0.first as? Bool ?? false }
return ControlEvent(events: source)
}
}
2,使用样例
(1)通过扩展,我们可以直接对 VC 的各种方法进行订阅。import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//页面显示状态完毕
self.rx.isVisible
.subscribe(onNext: { visible in
print("当前页面显示状态:\(visible)")
}).disposed(by: disposeBag)
//页面加载完毕
self.rx.viewDidLoad
.subscribe(onNext: {
print("viewDidLoad")
}).disposed(by: disposeBag)
//页面将要显示
self.rx.viewWillAppear
.subscribe(onNext: { animated in
print("viewWillAppear")
}).disposed(by: disposeBag)
//页面显示完毕
self.rx.viewDidAppear
.subscribe(onNext: { animated in
print("viewDidAppear")
}).disposed(by: disposeBag)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
(2)运行结果如下:

全部评论(1)
航哥新年快乐 辛苦了 恭喜发财
站长回复:谢谢,也祝你新年快乐,大吉大利