使用RxSwift定制UIControl子类

时间:2017-05-04 15:39:08

标签: swift subclass rx-swift uicontrol

我正在创建一个UIControl的自定义子类(我需要覆盖它的draw方法),我想添加RxSwift将其 isSelected 属性绑定到我的模型。

到目前为止一切顺利。这很好。

我的问题是如何更改 isSelected 属性值以响应用户touchUpInside事件?

我的第一次尝试是使用UIControl的 addTarget 方法,但ControlProperty不会以编程方式更改 isSelected 的值(如文档中所述)。但我可以想出另一种解决方法。

任何帮助表示感谢。

子类的源代码:

class SYYesNoButton: UIControl {

override init(frame: CGRect) {
    super.init(frame: frame)

    // subscribe to touchUpInside event

    addTarget(
        self,
        action: #selector(userDidTouchUpInside),
        for: UIControlEvents.touchUpInside)
}



func userDidTouchUpInside() {

    // change the value of the property
    // this does not work, 
    // the change is not reported to the ControlProperty
    // HOW CAN I CHANGE THIS ??

    self.isSelected = !isSelected
}

}

添加反应性支持的扩展程序:

extension SYYesNoButton {

    var rx_isSelected: ControlProperty<Bool> {

    return UIControl.valuePublic(
        self,
        getter: { (button) -> Bool in
            return button.isSelected
    },
        setter: { (button, value) in
            button.isSelected = value
    })


    }
}



extension UIControl {

    static func valuePublic<T, ControlType: UIControl>(_ control: ControlType, getter:  @escaping (ControlType) -> T, setter: @escaping (ControlType, T) -> ()) -> ControlProperty<T> {



        let values: Observable<T> = Observable.deferred { [weak control] in
            guard let existingSelf = control else {
                return Observable.empty()
            }

            return (existingSelf as UIControl).rx.controlEvent([.allEditingEvents, .valueChanged])
                .flatMap { _ in
                    return control.map { Observable.just(getter($0)) } ?? Observable.empty()
                }
                .startWith(getter(existingSelf))
        }


        return ControlProperty(values: values, valueSink: UIBindingObserver(UIElement: control) { control, value in
            setter(control, value)
        })

    }
}

谢谢大家。

2 个答案:

答案 0 :(得分:4)

如果您是UIControl的子类,那么您正在创建自己的控件类,并且必须覆盖beginTracking(_:with:)continueTracking(_:with:)endTracking(_:with:)或{{1}中的一个或多个使控件按照您想要的方式工作。然后使用正确的事件调用cancelTracking(with:)。 UIControl的内部不会有Rx。

从UIButton取一个队列,你的控件不应该自己选择,虽然它可以突出显示和不突出显示(例如,当用户的手指在它上面时)。

一旦您正确创建了UIControl,代码之外,控件就可以使用Rx来观察它,而无需您做额外的工作。

以下作品(针对Swift 5 / RxSwift 5进行了更新):

sendActions(for:)

答案 1 :(得分:2)

一旦有了实际的UIControl,就有一种更好的方法来使用RxCocoa中的帮助器方法来命名为ControlProperty的“本地” RxCocoa扩展。

例如:

extension Reactive where Base: someControl {
    var someProperty: ControlProperty<Float> {
        return controlProperty(editingEvents: .valueChanged,
                               getter: { $0.value },
                               setter: { $0.value = $1 })
    }
}

这将在触发指定的getter时公开UIControlEvent块中的当前值,并且每当绑定某个流时都将设置该值。

它的行为类似于Observable和Observer类型-您可以观察其值,但也可以订阅它。