【问题标题】:Custom UIControl subclass with RxSwift使用 RxSwift 自定义 UIControl 子类
【发布时间】:2017-10-02 21:31:23
【问题描述】:

我正在创建 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)
        })

    }
}

谢谢大家。

【问题讨论】:

  • 你也在使用RxCocoa吗?
  • 我也使用 RxCocoa

标签: swift subclass rx-swift uicontrol


【解决方案1】:

一旦你有了一个实际的 UIControl,就有一个更好的方法来使用 RxCocoa 中的辅助方法来实现一个称为 ControlProperty 的“原生”RxCocoa 扩展。

例如:

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

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

它有点像 Observable 和 Observer 类型一起使用 - 你可以观察它的值,但也可以订阅它。

【讨论】:

    【解决方案2】:

    如果您是 UIControl 的子类,那么您将创建自己的控件类,并且您必须覆盖 beginTracking(_:with:)continueTracking(_:with:)endTracking(_:with:)cancelTracking(with:) 中的一个或多个以使控件正常工作你要。然后使用正确的事件调用sendActions(for:)。 UIControl 的内部不会包含 Rx。

    从 UIButton 获取队列,您的控件不应选择自身,尽管它可以突出显示和取消突出显示自身(例如,当用户的手指在其上时。)

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

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

    class ViewController: UIViewController {
    
        @IBOutlet weak var yesNoButton: SYYesNoButton!
        private let bag = DisposeBag()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            yesNoButton.rx.controlEvent(.touchUpInside)
                .scan(false) { v, _ in !v }
                .bind(to: yesNoButton.rx.isSelected)
                .disposed(by: bag)
        }
    }
    
    @IBDesignable
    class SYYesNoButton: UIControl {
    
        override func layoutSubviews() {
            super.layoutSubviews()
            backgroundColor = isSelected ? .green : .red
        }
    
        override var isSelected: Bool {
            didSet {
                super.isSelected = isSelected
                backgroundColor = isSelected ? .green : .red
            }
        }
    }
    

    【讨论】:

    • 丹尼尔,你能解释一下.scan(false, accumulator: { !$0.0 })这行吗?我似乎无法在 Swift 5 和当前版本的 RxSwift 中编译它。谢谢。
    • 我已经更新了答案。 Swift 现在要求您在忽略进入闭包的值时显式调用。
    猜你喜欢
    • 2011-02-23
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多