搭建UI
搭建下图UI:
UI
UI控件处理
简单控件处理
首先定义一个用于显示信息的函数:
func debug(_ param: String) {
print(param)
label.text = param
}
UITapGestureRecognizer处理:
pan.rx.event.subscribe(onNext: {
if self.textField.isFirstResponder || self.textField1.isFirstResponder || self.textView.isFirstResponder || self.textView1.isFirstResponder {
self.view.endEditing(true)
} else {
self.debug("UIGestureRecognizer event \($0.state.rawValue)")
}
}).disposed(by: bag)
UIBarButtonItem处理:
item.rx.tap.bind {
self.debug("UIBarButtonItem Tapped")
}.disposed(by: bag)
UISlider处理:
slider.rx.value.subscribe(onNext: {
self.debug("UISlider value \($0)")
}).disposed(by: bag)
UIButton处理:
button.rx.tap.subscribe(onNext: {
self.debug("UIButton Tapped")
}).disposed(by: bag)
UIDatePicker处理:
datePicker.rx.date.subscribe(onNext: {
self.debug("UIDatePicker date \($0)")
}).disposed(by: bag)
双向绑定
双向绑定是通过BehaviorRelay来实现的,定义如下<->
操作符方便UI控件的ControlProperty序列的双向绑定:
func <-><T>(property: ControlProperty<T>, relay: BehaviorRelay<T>) -> Disposable {
#if DEBUG
if T.self == String.self {
fatalError("删除这个信息也是可以的,但是这是在提醒开发者有可能试着将一些“rx.text”属性绑定到relay。\n" +
"这通常能够很好的工作,但是对于一些IME语言这种简单的方法将造成意外的问题,因为当文本正在输入时将会返回中间结果。\n" +
"解决方案: 就是使用 `textField <-> relay` 替换 `textField.rx.text <-> relay`.\n" +
"了解更多: https://github.com/ReactiveX/RxSwift/issues/649\n")
}
#endif
let bind = relay.bind(to: property)
let bindRelay = property.subscribe(onNext: { (n) in
relay.accept(n)
}, onCompleted: {
bind.dispose()
})
return Disposables.create(bind, bindRelay)
}
代码分析:
- 将BehaviorRelay序列的值绑定到ControlProperty序列上
- 订阅ControlProperty序列的值
- ControlProperty序列发出元素时BehaviorRelay序列也通过
accept
操作符发出元素,完成时通过dispose操作符清理BehaviorRelay序列 - 返回订阅ControlProperty序列的Disposable
注意:
在这里有一个问题就是当对字符串文本进行双向绑定时,因为当文本正在输入时将会返回中间结果,对于一些IME语言这种简单的方法将造成意外的问题。解决方案就是使用 textField <-> relay
替换 textField.rx.text <-> relay
。
具体代码代码实现:
func <-><Base>(textInput: TextInput<Base>, relay: BehaviorRelay<String>) -> Disposable {
let bind = relay.bind(to: textInput.text)
let bindRelay = textInput.text.subscribe(onNext: { (n) in
if let nonMarkedText = nonMarkedText(textInput.base), nonMarkedText != relay.value {
relay.accept(nonMarkedText)
}
}, onCompleted: {
bind.dispose()
})
return Disposables.create(bind, bindRelay)
}
UISegmentedControl处理:
let value = BehaviorRelay(value: 0)
_ = segmentedControl.rx.value <-> value
value.asObservable().subscribe(onNext: { (v) in
self.debug("UISegmentedControl value \(v)")
}).disposed(by: bag)
UISwitch处理:
let switchV = BehaviorRelay(value: true)
_ = switcher.rx.value <-> switchV
switchV.asObservable().subscribe(onNext: {
self.debug("UISwitch value \($0)")
}).disposed(by: bag)
switcher.rx.value.bind(to: activityIndicator.rx.isAnimating).disposed(by: bag)
UITextField处理:
// MARK: UITextField
// 在ios 11.2中存在内存泄漏
//
// 最终的类UITextFieldSubclass: UITextField { deinit { print("不会调用") } }
if #available(iOS 11.2, *) {
let textV = BehaviorRelay(value: "")
_ = textField.rx.textInput <-> textV
textV.asObservable().subscribe(onNext: {
self.debug("UITextField text \($0)")
}).disposed(by: bag)
let textV1 = BehaviorRelay<NSAttributedString?>(value: NSAttributedString(string: ""))
_ = textField1.rx.attributedText <-> textV1
textV1.asObservable().subscribe(onNext: {
self.debug("UITextField attributedText \($0?.description ?? "")")
}).disposed(by: bag)
}
注意:在ios 11.2
中存在内存泄漏,继承自UITextField
最终的类不会执行deinit
函数。
UITextView处理:
let textViewV = BehaviorRelay(value: "")
_ = textView.rx.textInput <-> textViewV
textViewV.asObservable().subscribe(onNext: {
self.debug("UITextView text \($0)")
}).disposed(by: bag)
let textViewV1 = BehaviorRelay<NSAttributedString?>(value: NSAttributedString(string: ""))
_ = textView1.rx.attributedText <-> textViewV1
textViewV1.asObservable().subscribe(onNext: {
self.debug("UITextView attributedText \($0?.description ?? "")")
}).disposed(by: bag)
网友评论