(四)RxSwift之TextField和TextView的区别

1,623 阅读2分钟

今天我们来讨论一个小细节,我们常常会遇见这样的需求,一个按钮根据所有的输入框是否为空来显示灰色或者高亮。如下图所示:

这个看起来没有任何的难度,在textField的代理中判断,当所有输入框的长度大于0的时候,按钮就高亮。但是实施起来的时候有点小问题,当我们点击地址簿,选择地址回来以后并没有触发textField的代理。这是为什么呢?

textField.addTarget(self, action: #selector(valueChange), for: .allEvents)
textView.delegate = self

@objc func valueChange() {
    print("----textField系统的方法")
}
func textViewDidChange(_ textView: UITextView){
    print("----textView系统的方法")
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    textField.text = "8d8dc8d78d3bcf7caef7c5a8d7d2484b486f"
    textView.text = "描述"
}

这是系统的方法,当我们点击view的时候textFiledTextView都赋上了值,但是都没有触发代理方法。

让我们看一下,RxSwift封装的是否触发:

textField.rx.text.orEmpty.skip(1).subscribe(onNext: { (text) in
    print("textField:", text)
}).disposed(by: disposebag)

textView.rx.text.orEmpty.skip(1).subscribe(onNext: { (text) in
    print("textView:", text)
}).disposed(by: disposebag)

打印

textView: 描述

我们发现TextFiled没有触发,TextView触发了,这是为什么吗?让我们一起走进源码

TextFiled:

/// This is a separate method to better communicate to public consumers that
/// an `editingEvent` needs to fire for control property to be updated.
internal func 'controlPropertyWithDefaultEvents'<T>(
    editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged],
    getter: @escaping (Base) -> T,
    setter: @escaping (Base, T) -> Void
    ) -> ControlProperty<T> {
    return controlProperty(
        editingEvents: editingEvents,
        getter: getter,
        setter: setter
    )
}

由代码可知,RxSwiftTextFiled封装的是系统的Event,所以系统没触发,他也不会触发。

再看看为什么RxSwiftTextView可以触发

/// Reactive wrapper for `text` property.
public var value: ControlProperty<String?> {
let source: Observable<String?> = Observable.deferred { [weak textView = self.base] in
    let text = textView?.text
    
    let textChanged = textView?.textStorage
        // This project uses text 'storage notifications' because
        // that’s the only way to catch autocorrect changes
        // in all cases. Other suggestions are welcome.
        .rx.'didProcessEditingRangeChangeInLength'
        // This observe on is here because text storage
        // will emit event while process is not completely done,
        // so rebinding a value will cause an exception to be thrown.
        .observeOn(MainScheduler.asyncInstance)
        .map { _ in
            return textView?.textStorage.string
        }
        ?? Observable.empty()
    
    return textChanged
        .startWith(text)
}

我们看见了RxSwift封装的是通知,所以会打印

那我们应该如何解决呢,其实方法很简单,只要在赋值的地方加一句代码就OK了,重新触发一下

// send all actions associated with events
textField.sendActions(for: .allEvents)