问题:对textField进行订阅,发现点击textField会默认先执行了两次,一次是程序启动后textField初始化时,一次是成为第一响应时,如下:
textFiled.rx.text.subscribe(onNext: { (text) in
print("text== \(text ?? "")")
})
.disposed(by: disposeBag)
--------------输入12,打印,为什么会有两次为空的打印??----------------
text==
text==
text== 1
text== 12
问题二:直接修改textFiled.text,为什么没有触发订阅??(textView会触发)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
textFiled.text = "touche--textField值改变"
}
首先为什么textFiled
和textView
有区别?
------------textView.rx.text内部------------
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内部------------
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
的通知是监听文本的改变发送通知,textField
则是event
事件触发的,所以不一样,直接修改值并没有触发event
事件。textField
的Event
中的valueChanged
其实是没有意义的,因为textField
并没有valueChanged
的event
事件(滑块就有),放在这只是因为UIControl
的枚举中有这个valueChanged
而已。
解决textField值改变后能响应事件的办法:改变值后添加代码
textFiled.sendActions(for: .allEditingEvents)
,意思就是发生一个allEditingEvents
事件出去。
下面主要介绍下为什么会textField
有两次触发,源代码如下:
-----------------进入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)
}
进入到上面的核心代码,好长,这样来看就好多了:
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
创建序列,通过Binder
订阅创建观察者(和subscribe
类似的一个方法),所以会执行第一次observer.on
(代码里面我标记了),所以会输出一次,我们叫做是rx的textField初始化。
第二次则是在我们点击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)))
,我们想要知道的就是闭包在什么时候回被调用,上代码:
----------------ControlTarget的实现----------------
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
,所以我们希望这个方法被调用。
control.addTarget(self, action: selector, for: controlEvents)
给textField
绑定了一个事件selector
,通过let selector: Selector = #selector(ControlTarget.eventHandler(_:))
,selector
指向eventHandler
,eventHandler
调用了callback(control)
返回自身,so 闭包被调用了,问题完美解决。
总结:第一次是初始化调用
observer.on
引起的;第二次是点击了textField
引起的event
事件响应。如有写的不对的地方,烦请不吝赐教,本人不胜感激~~~
网友评论