[RxSwift教程]19、特征序列3:ControlProperty、 ControlEvent
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(let_us_code)
➤博主域名:https://www.zengqiang.org
➤GitHub地址:https://github.com/strengthen/LeetCode
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
强烈推荐!!!试用博主个人App作品!提需求!提建议!
App Store搜索:【Mind Draft】
中国区可直接点击跳转:【Mind Draft】
五、ControlProperty
1,基本介绍
(1)ControlProperty 是专门用来描述 UI 控件属性,拥有该类型的属性都是被观察者(Observable)。
(2)ControlProperty 具有以下特征:
- 不会产生 error 事件
- 一定在 MainScheduler 订阅(主线程订阅)
- 一定在 MainScheduler 监听(主线程监听)
- 共享状态变化
2,使用样例
(1)其实在 RxCocoa 下许多 UI 控件属性都是被观察者(可观察序列)。比如我们查看源码(UITextField+Rx.swift),可以发现 UITextField 的 rx.text 属性类型便是 ControlProperty<String?>:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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 上,即前者作为被观察者,后者作为观察者。可以这么写:
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
|
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>:
1
2
3
4
5
6
7
8
|
import RxSwift import UIKit extension Reactive where Base : UIButton { public var tap: ControlEvent < Void > { return controlEvent(.touchUpInside) } } |
(2)那么我们如果想实现当一个 button 被点击时,在控制台输出一段文字。即前者作为被观察者,后者作为观察者。可以这么写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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 序列属性,方便对视图的释放进行订阅。
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
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 的各种方法进行订阅。
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
|
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)运行结果如下: