天天看点

RxSwift UI控件扩展

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)
    }