RxSwift學(xué)習(xí)插曲--UITextField的兩次輸出

前言

在使用Swift的過程中,應(yīng)該都使用過UITextField這個控件,這一篇就來對這個控件在RxSwift中的使用做個淺析。

問題

先來寫一個UITextFieldRxSwift中的基本語法:

@IBOutlet weak var textFiled: UITextField!

textFiled.rx.text.subscribe(onNext: { (text) in
        print("你輸入的是: \(text)")
    })

command+R運行代碼,此時先不做任何交互操作,會發(fā)現(xiàn)打印出了你輸入的是: Optional(""),然后點擊textFiled準備輸入內(nèi)容,這時候再次打印了你輸入的是: Optional(""),

image

之后輸入內(nèi)容,正常打印出的就是輸入的內(nèi)容,

image

也就是說在textFiled輸入內(nèi)容前,打印了兩次空的內(nèi)容,難道這是RxSwiftbug??.

再來看另外一個問題:

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textFiled.text = "i miss you"
    }

在點擊屏幕的時候,我們給textFiled賦值,看是否能訂閱到賦值內(nèi)容;
運行代碼會發(fā)現(xiàn)并沒有訂閱到賦值的內(nèi)容,但是明明已經(jīng)對textFiledtext進行訂閱了,text值有改變的話,居然訂閱不到?

分析

首先,點擊textFiled.rx.texttext進去看源碼流程,

/// Reactive wrapper for `text` property.
 public var text: ControlProperty<String?> {
        return value
}

這里返回了value,相當于是valuegetter方法,再點擊value跟進去:

/// Reactive wrapper for `text` property.
    public var value: ControlProperty<String?> {
        return base.rx.controlPropertyWithDefaultEvents(
            getter: { textField in
                textField.text
            },
            setter: { textField, value in
                // This check is important because setting text value always clears control state
                // including marked text selection which is imporant for proper input 
                // when IME input method is used.
                if textField.text != value {
                    textField.text = value
                }
            }
        )
    }

這里返回了controlPropertyWithDefaultEvents()方法,而且這個方法傳入了兩個參數(shù)閉包,gettersetter,再跟進去controlPropertyWithDefaultEvents()這個方法,

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

可以看到這個controlPropertyWithDefaultEvents()方法有三個參數(shù),默認參數(shù)[.allEditingEvents, .valueChanged],以及傳入的getter閉包,setter閉包;而這個方法會返回controlProperty()方法,再次跟進去看一下這個controlProperty()方法的實現(xiàn);

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)
        let bindingObserver = Binder(base, binding: setter)

        return ControlProperty<T>(values: source, valueSink: bindingObserver)
    }

如果在這個方法里打斷點可以發(fā)現(xiàn),這里是先執(zhí)行·return ControlProperty(),然后才會來到Observable.create()創(chuàng)建序列的閉包內(nèi)執(zhí)行,也就是說只要執(zhí)行textFiled.rx.text.subscribe(),就必然會進入到controlProperty()方法中,在這個方法里,創(chuàng)建序列的閉包方法內(nèi),會執(zhí)行observer.on(.next(getter(control)))這句代碼,這句代碼就會執(zhí)行一次.next,也就是說明會發(fā)送一次onNext信號,(這里就是執(zhí)行的第一次打印空白的地方)

跟著代碼繼續(xù)往下看,會創(chuàng)建一個controlTarget,跟進去源碼看一下controlTarget的初始化;

// This should be only used from `MainScheduler`
final class ControlTarget: RxTarget {
    typealias Callback = (Control) -> Void
    let selector: Selector = #selector(ControlTarget.eventHandler(_:))
    weak var control: Control?
#if os(iOS) || os(tvOS)
    let controlEvents: UIControl.Event
#endif
    var callback: Callback?
    #if os(iOS) || os(tvOS)
    init(control: Control, controlEvents: UIControl.Event, callback: @escaping Callback) {
        MainScheduler.ensureRunningOnMainThread()
        self.control = control
        self.controlEvents = controlEvents
        self.callback = callback
        super.init()
        control.addTarget(self, action: selector, for: controlEvents) // 添加事件 綁定eventHandler方法
        let method = self.method(for: selector)
        if method == nil {
            rxFatalError("Can't find method")
        }
    }
    @objc func eventHandler(_ sender: Control!) {
        if let callback = self.callback, let control = self.control {
            callback(control)
        }
    }
}

controlTargetinit方法中,傳入了三個參數(shù):control,[.allEditingEvents, .valueChanged],以及傳入的閉包{ _ in if let control = weakControl { observer.on(.next(getter(control))) } },這里會先將這三個參數(shù)保存下來,然后執(zhí)行control.addTarget(self, action: selector, for: controlEvents),給傳入的control添加事件,綁定方法,這里的control就是創(chuàng)建的textFiled,也就是給textFiled添加事件綁定方法,只要它開始享有就會執(zhí)行selector方法,而此處綁定的selector#selector(ControlTarget.eventHandler(_:))方法,也就是說事件觸發(fā)會來到ControlTarget.eventHandler(_:),在eventHandler(_:)方法內(nèi),執(zhí)行callback(control)執(zhí)行閉包;

{ _ in
      if let control = weakControl {
        observer.on(.next(getter(control)))
        }
    }

在閉包內(nèi)執(zhí)行observer.on(.next(getter(control))),這里再次.next,也就是說明會發(fā)送一次onNext信號,(這里就是執(zhí)行的第二次打印空白的地方)

總結(jié)

從上面的分析中可以知道,對于問題1輸出兩次空白內(nèi)容:在textFiled.rx.text訂閱的時候,會執(zhí)行一次onNext,發(fā)送信號,輸出一次空白內(nèi)容,在點擊textFiled響應(yīng)的時候會執(zhí)行一次onNext,發(fā)送信號,輸出一次空白內(nèi)容,那么就有必要忽略掉第一次輸出,解決方法是使用skip()方法;

textFiled.rx.text.skip(1).subscribe(onNext: { (text) in
        print("你輸入的是: \(text)")
    })

對于問題2:textFiledtext進行賦值,subscribe閉包不執(zhí)行,問題在于RxSwift的底層是event的封裝,而對textFiledtext進行賦值并不是一個event事件,而且也不是一個KVO的形式,解決這個問題的方法可以在RxSwiftGitHub Issues討論區(qū)里面看到,利用sendActions(for: .allEditingEvents)方法:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textFiled.text = "i miss you"
    }
textFiled.sendActions(for: .allEditingEvents)
image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容