問題:對textField進(jìn)行訂閱,發(fā)現(xiàn)點(diǎn)擊textField會默認(rèn)先執(zhí)行了兩次迟杂,一次是程序啟動后textField初始化時泌枪,一次是成為第一響應(yīng)時耳贬,如下:
textFiled.rx.text.subscribe(onNext: { (text) in
print("text== \(text ?? "")")
})
.disposed(by: disposeBag)
--------------輸入12,打印醋旦,為什么會有兩次為空的打雍闼?饲齐?----------------
text==
text==
text== 1
text== 12
問題二:直接修改textFiled.text,為什么沒有觸發(fā)訂閱钉凌??(textView會觸發(fā))
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
textFiled.text = "touche--textField值改變"
}
首先為什么textFiled
和textView
有區(qū)別捂人?
------------textView.rx.text內(nèi)部------------
public var text: ControlProperty<String?> {
return value
}
/// 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
//注釋中提到了御雕,使用的是通知notifications矢沿,歡迎其他建議.
// 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)
}
------------textField.rx.text內(nèi)部------------
internal func controlPropertyWithDefaultEvents<T>(
//通過event事件處理的,而且就兩個 [.allEditingEvents, .valueChanged],
editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged],
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
return controlProperty(
editingEvents: editingEvents,
getter: getter,
setter: setter
)
}
textView
的通知是監(jiān)聽文本的改變發(fā)送通知酸纲,textField
則是event
事件觸發(fā)的捣鲸,所以不一樣,直接修改值并沒有觸發(fā)event
事件闽坡。textField
的Event
中的valueChanged
其實(shí)是沒有意義的栽惶,因?yàn)?code>textField并沒有valueChanged
的event
事件(滑塊就有),放在這只是因?yàn)?code>UIControl的枚舉中有這個valueChanged
而已疾嗅。
解決textField值改變后能響應(yīng)事件的辦法:改變值后添加代碼
textFiled.sendActions(for: .allEditingEvents)
外厂,意思就是發(fā)生一個allEditingEvents
事件出去。
下面主要介紹下為什么會textField
有兩次觸發(fā),源代碼如下:
-----------------進(jìn)入textFiled.rx.text方法-----------------
public var text: ControlProperty<String?> {
return value
}
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
}
}
)
}
----------------- base.rx.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
)
}
----------------- controlProperty 最最核心的代碼代承,所有的答案都在這里-----------------
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
observer.on(.next(getter(control)))
let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in
if let control = weakControl {
// 第二次observer.on
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)
}
進(jì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 {...}
.takeUntil(deallocated)
let bindingObserver = Binder(base, binding: setter)
return ControlProperty<T>(values: source, valueSink: bindingObserver)
}
通過create
創(chuàng)建序列次泽,通過Binder
訂閱創(chuàng)建觀察者(和subscribe
類似的一個方法)穿仪,所以會執(zhí)行第一次observer.on
(代碼里面我標(biāo)記了),所以會輸出一次意荤,我們叫做是rx的textField初始化啊片。
第二次則是在我們點(diǎn)擊textField
時,主要分析代碼:
let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in
if let control = weakControl {
observer.on(.next(getter(control)))
}
}
方法傳入了control
玖像,event
和一個閉包observer.on(.next(getter(control)))
紫谷,我們想要知道的就是閉包在什么時候回被調(diào)用,上代碼:
----------------ControlTarget的實(shí)現(xiàn)----------------
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)
let method = self.method(for: selector)
if method == nil {
rxFatalError("Can't find method")
}
}
#elseif os(macOS)
init(control: Control, callback: @escaping Callback) {...}
#endif
@objc func eventHandler(_ sender: Control!) {
if let callback = self.callback, let control = self.control {
callback(control)
}
}
override func dispose() {...}
}
通過init
初始化后捐寥,看到閉包指向了Callback
笤昨,也就是self.callback
,所以我們希望這個方法被調(diào)用握恳。
control.addTarget(self, action: selector, for: controlEvents)
給textField
綁定了一個事件selector
瞒窒,通過let selector: Selector = #selector(ControlTarget.eventHandler(_:))
,selector
指向eventHandler
乡洼,eventHandler
調(diào)用了callback(control)
返回自身崇裁,so 閉包被調(diào)用了,問題完美解決束昵。
總結(jié):第一次是初始化調(diào)用
observer.on
引起的拔稳;第二次是點(diǎn)擊了textField
引起的event
事件響應(yīng)。如有寫的不對的地方锹雏,煩請不吝賜教巴比,本人不勝感激~~~