RxSwift探究的TextField的内部实现序列

423 阅读2分钟

RxSwift中的textFiled的使用

let textFiled = UITextField()
textFiled.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 300, height: 200))
textFiled.backgroundColor = UIColor.red
self.view.addSubview(textFiled)
   
let _ = textFiled.rx.text.subscribe(onNext: { (content) in
  if let tmpString = content{
      print("收到消息 -------" + tmpString)
  }
}, onError: { (error) in
  print("\(error)")
}, onCompleted: {
  print("completed ...")
}) {
  print("disposed...")
}

在我们的程序启动后,我们就收到一次订阅,这是为什么,在哪里触发一次消息发送,先对这个时间进行探究,从.text进入RxSwift中的UItextFieid+RxSwift.swift我们看到了对应的代码实现,如下


extension Reactive where Base: UITextField {
    /// Reactive wrapper for `text` property.
    public var text: ControlProperty<String?> {
        return 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
                }
            }
        )
    }
    // 省略其他代码
    // ....
}

代码分析 我们使用的text属性是继承自ControlProperty结构体的属性,其返回的数据是valuevaluegetter方法textfieldrx属性的controlPropertyWithDefaultEvents方法,在这个方法中有对应的value值的返回,

代码片1

// 代码片1
internal func controlPropertyWithDefaultEvents<T>(
   editingEvents: UIControlEvents = [.allEditingEvents, .valueChanged],
   getter: @escaping (Base) -> T,
   setter: @escaping (Base, T) -> Void
   ) -> ControlProperty<T> {
   return controlProperty(
       editingEvents: editingEvents,
       getter: getter,
       setter: setter
   )
}

// 代码片2
public func controlProperty<T>(
   editingEvents: UIControlEvents,
   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)
}

结合这两段代码,代码片1中我们规定了两种事件类型,分别是.allEditingEvents.valueChanged

  • 在代码片2中创建序列的时,我们可以看到observer.on(.next(getter(control)))这种类型的event它是,订阅了我们UI空间的初始化event;
  • let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) {...}这段代码的实现是尾随的逃逸闭包,下面我来看内部的实现
    
    	 init(control: Control, controlEvents: UIControlEvents, 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")
    	        }
        }
    
    
    
    代码分析 这里边,其实就是将,UItextFieldevent类型的触发方法内部绑定到了textField上,这个触发方法就是eventHandler,在其中就会调用callBack,通过这样的方式就订阅到了textfield的所有的触发事件 然后再通过let bindingObserver = Binder(base, binding: setter)textfield的赋值event将其定义成绑定观察者,最后在最外层subscribe订阅触发对应的event的事件处理

以上代码多有,不正确之处和写的不详细的地方,欢迎大家扶正和批评,谢谢