RxSwift UI控件扩展
最好的示例是参考RxCocoa查看类似的属性如何扩展Rx化的。
为了配合RxSwift的绑定关系,RxCocoa提供简单的基于Cocoa控件的扩展,但是很少,比如Label的属性有很多,然而,RxCocoa只实现了text和attribute属性的观察者。所有部分常用的我们可以自定义,或者自定义/第三方控件可以通过扩展来支持Rx化。
Binder
Binder作为特殊观察者,具备主线程绑定,不处理错误,简直就是为UI控件而生。
RxCocoa实现UILabel text的观察者属性
//RxCocoas -> UILabel+Rx.swift
import RxSwift
import UIKit
extension Reactive where Base: UILabel {
/// Bindable sink for `text` property.
public var text: Binder<String?> {
return Binder(self.base) { label, text in
label.text = text
}
}
}
为UILabel添加文本颜色观察者
extension Reactive where Base: UILabel {
public var textColor: Binder<UIColor> {
return Binder(self.base) { (label, color) in
label.textColor = color
}
}
}
以下是Binder的初始化方法
/// Initializes `Binder`
///
/// - parameter target: 目标对象
/// - parameter scheduler: 订阅线程,默认主线程
/// - parameter binding: 绑定逻辑(返回目标对象和监听值)
public init<Target: AnyObject>(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping (Target, Value) -> Void) {
weak var weakTarget = target
self._binding = { event in
switch event {
case .next(let element):
_ = scheduler.schedule(element) { element in
if let target = weakTarget {
binding(target, element)
}
return Disposables.create()
}
case .error(let error):
bindingError(error) // 输出错误日志
case .completed:
break // 不处理
}
}
}
所以,利用Binder为控件扩展观察属性非常方便。
ControlEvent
ControlEvent是被观察者,主要用于UI发出的事件,即将UI的事件或者属性转化为序列。
我的感觉就是ControlEvent将方法转化为序列输出。
比如将Controller的viewDidAppear Rx化
public var viewDidAppear: ControlEvent(Bool) {
let source = self.methodInvoked(#selector(Base.viewDidAppear)).map{ $0.first as? Bool ?? false}
return ControlEvent(event:source)
}
ControlEvent包裹着一个被观察者source
参考:
RxSwift特征序列ControlPoperty、CotrolEvent
RxGestrue
另外可以参考RxCocoa的UIButton+Rx.swift 和UITableView+Rx.swift对应ControlEvent实现,都是通过中间中转。
ControlProperty
ControlProperty即是观察者,也是被观察者。所有controlProperty就有一个setter(观察)和getter(被观察)
ControlProperty初始化方法
public init<Values: ObservableType, Sink: ObserverType>(values: Values, valueSink: Sink) where Element == Values.Element, Element == Sink.Element {
self._values = values.subscribeOn(ConcurrentMainScheduler.instance)
self._valueSink = valueSink.asObserver()
}
所以可以看出,只要传入一个观察者(一般是Binder)和一个被观察者即可
RxCocoa -> UICotrol+Rx.swift实现的UIControl的ControlProperty
/// Creates a `ControlProperty` that is triggered by target/action pattern value updates.
///
/// - parameter controlEvents: Events that trigger value update sequence elements.
/// - parameter getter: Property value getter.
/// - parameter setter: Property value setter.
public func controlProperty<T>(
editingEvents: UIControl.Event,
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
let source: Observable<T> = Observable.create { [weak weakControl = base] observer in
guard let control = weakControl else {
observer.on(.completed)
return Disposables.create()
}
observer.on(.next(getter(control)))
let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in
if let control = weakControl {
observer.on(.next(getter(control)))
}
}
return Disposables.create(with: controlTarget.dispose)
}
.takeUntil(deallocated) // source 被观察者
let bindingObserver = Binder(base, binding: setter) // 观察者
return ControlProperty<T>(values: source, valueSink: bindingObserver)
}