UITextField和一个UILabel绑定 浅析
转载自:http://fengdeng.github.io/blog/2016/01/22/rxswift-dao-di-%5B%3F%5D-ge-uitextfieldshi-ru-he-he-%5B%3F%5D-ge-uilabelbang-ding-de/
多看多揣摩吧!
只要一行代码
writeTextField.rx_text.bindTo(displayLabel.rx_text)
看下效果

那么这到底是如何实现的呢
整体分析
代码层:
writeTextField的rx_text通过bingTo这个函数和displayLabel的rx_text绑定了。
Rx思想:
一个被观察者(Observable)通过bingTo函数和一个观察者(Observer)绑定了,这样无论被观察这如何变化,观察者都了如指掌
深层解析
writeTextField.rx_text
这是一个Observable。也是一个Observer。 rx_text是RxSwift给UITextField的一个拓展属性,上代码
public var rx_text: ControlProperty<String> {
return rx_value(getter: { [weak self] in
self?.text ?? ""
}, setter: { [weak self] value in
self?.text = value
})
}
rx_text是一个ControlProperty<String>。由后面的函数生成。我们去看下那个函数
func rx_value<T: Equatable>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
let source: Observable<T> = Observable.create { [weak self] observer in
guard let control = self else {
observer.on(.Completed)
return NopDisposable.instance
}
observer.on(.Next(getter()))
let controlTarget = ControlTarget(control: control, controlEvents: [.AllEditingEvents, .ValueChanged]) { control in
observer.on(.Next(getter()))
}
return AnonymousDisposable {
controlTarget.dispose()
}
}
.distinctUntilChanged()
.takeUntil(rx_deallocated)
return ControlProperty<T>(values: source, valueSink: AnyObserver { event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
setter(value)
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
})
}
这个函数是UIControl的,所有UIControl的所有子类都有哈。UITextField是UIControl的子类
函数有点长,我们一步一步分析,其实就两句
先说下第一句,source的生成过程。source是一个Observable,即这里的Observable
- 首先获取self,获取不到直接completed
- 获取到了,直接Next一个刚刚传进来的getter。这里是
self?.text ?? "",即流走一个Observable - 生成一个
ControlTarget,这个ControlTarget每当ValueChanged都会流走一个Observable(这个ControlTarget有点多,下回讲) - 上面三步走完后 ,这里的是一个Observable。对这个Observable进行
distinctUntilChanged(),这个函数说明,只有String改变才会继续往下流 takeUntil(rx_deallocated),直到这个UITextField被释放- source结束,source的这个流程就是上面的5步
其实如果一个UITextField的rx_text作为一个Observable,上面的代码已经足够了。
为了实现UITextField也可以作为observer,于是有了下面的代码。
再说第二句,通过source,生成一个ControlProperty<String>。这个被观察者才是本函数要返回的类型。它既是一个Observer,也是一个Observable
- 直接调用
ControlProperty<PropertyType>的初始化方法生成 看一下初始化方法带进去的一个闭包
AnyObserver { event in
MainScheduler.ensureExecutingOnScheduler() switch event {
case .Next(let value):
setter(value)
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
因为UI操作都必须在主线程。MainScheduler.ensureExecutingOnScheduler()这一句话是判断当前线程是不是在主线程
下面的代码是当UITextField作为一个observer时,接收到一个流的响应。
displayLabel.rx_text
这是一个Observer rx_text是RxSwift给UILabel的一个拓展属性,上代码
public var rx_text: AnyObserver<String> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
}
很简单,只有响应流的代码。有流过来,就self?.text = value
有同学可能有疑问了,UITextField可以被观察,也可以是观察者。UILabel只能是观察者,我要是想观察UILabel的text怎么办呢???
哈哈,easy,please try KVO
displayLabel.rx_observe(String.self, "text").subscribe { (event) -> Void in
switch event{
case .Next(let m):
print(m)
case .Completed:
print("")
case .Error(let t):
print(t)
}
}
KVO还是比较简单的,大家可以自己看看。或许下一次我写写
bindTo
bingTo是Observable的拓展
public func bindTo<O: ObserverType where O.E == E>(observer: O) -> Disposable {
return self.subscribe(observer)
}
内部调用的Observable的subscribe方法来和一个observer进行关联
这里就是调用了ControlProperty的subscribe方法.
public func subscribe<O : ObserverType where O.E == E>(observer: O) -> Disposable {
return _values.subscribe(observer)
}
_values.subscribe(observer)调用了SubscribeOn这个类的subscribe方法,
SubscribeOn没实现subscribe方法,于是去他的父类Producer里去调用了
override func subscribe<O : ObserverType where O.E == Element>(observer: O) -> Disposable {
if !CurrentThreadScheduler.isScheduleRequired {
return run(observer)
}
else {
return CurrentThreadScheduler.instance.schedule(()) { _ in
return self.run(observer)
}
}
}
Producer又去它的子类SubscribeOn去调用了run方法
override func run<O : ObserverType where O.E == Ob.E>(observer: O) -> Disposable {
let sink = SubscribeOnSink(parent: self, observer: observer)
sink.disposable = sink.run()
return sink
}
当UITextField的每一个值产生时,调用了下面这个方法,在SubscribeOnSink这个类中
func on(event: Event<Element>) {
forwardOn(event)
if event.isStopEvent {
self.dispose()
}
}
forwardOn(event)这个方法又去SubscribeOnSink这个类的父类中去调用了
final func forwardOn(event: Event<O.E>) {
if disposed {
return
}
_observer.on(event)
}
最终调到了_observer.on(event)这一句话。也是就UILabel的rx_text的on方法,on(event)是一个闭包,在UILabel的rx_text中等同以下代码
{ [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
以上是一个复杂的继承
UITextField和一个UILabel绑定 浅析的更多相关文章
- Javascript 事件对象(四)一个事件绑定多个不同的函数
给一个对象绑定多个事件处理函数: <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-T ...
- 配置Nginx支持SSL SNI(一个IP绑定多个证书) 以及Haproxy实现多域名证书
概述 传统的每个SSL证书签发,每个证书都需要独立ip,假如你编译openssl和nginx时候开启TLS SNI (Server Name Identification) 支持,这样你可以安装多个S ...
- 封装一个UILabel圆形边框显示进度
封装了一个UILabel并让它显示圆形的边框,UILabel上面显示百份比,而边框则用Animation绘制到整个圆占指定百分比的点. 这只是我个人想的继承一个UILabel实现的,用到两个CASha ...
- APACHE如何里一个站点绑定多个域名?用ServerAlias
APACHE2如何里一个站点绑定多个域名?用ServerAlias以前很笨,要使多个域名指向同一站点总是这样写: <VirtualHost *:80>ServerAdmin i@kuigg ...
- APACHE如何里一个站点绑定多个域名?用ServerAlias servername
APACHE2如何里一个站点绑定多个域名?用ServerAlias以前很笨,要使多个域名指向同一站点总是这样写: <VirtualHost *:80>ServerAdmin i@kuigg ...
- u3d一个GameObject绑定两个AudioSource
u3d 一个GameObject绑定两个AudioSource ,使他们分别播放,并控制 using UnityEngine; using System.Collections; public cl ...
- APACHE如何一个站点绑定多个域名?
大家肯定遇到过这样的情况,需要APACHE2里一个站点绑定多个域名,那么如何操作呢?用ServerAlias 以前很笨,要使多个域名指向同一站点总是这样写: ServerAdmin admin@dom ...
- 用ES6的class模仿Vue写一个双向绑定
原文地址:用ES6的class模仿Vue写一个双向绑定 点击在线尝试一下 最终效果如下: 构造器(constructor) 构造一个TinyVue对象,包含基本的el,data,methods cla ...
- APACHE如何里一个站点绑定多个域名?用ServerAlias 转
APACHE2如何里一个站点绑定多个域名?用ServerAlias以前很笨,要使多个域名指向同一站点总是这样写:<VirtualHost *:80>ServerAdmin i@kuigg. ...
随机推荐
- 5、Web应用程序中的安全向量 -- Open Redirect Attack(开放重定向)
开放重定向攻击的概念:那些通过请求(如查询字符串和表单数据)指定重定向URL的Web应用程序可能会被篡改,而把用户重定向到外部的恶意URL. 在执行重定向之前需先检查目标地址的有效性,可使用Url.I ...
- IOS 中会发生crash的操作
对字典和数组进行下列操作时会产生crash: 对于字典来说: 查询时,key=nil 或者 key=null 时都能正常运行 插入时,,key=nil 或者 key=null 都会crash 对于数组 ...
- Struts入门学习(一)
刚开始学习框架的时候感觉很简单,都是用到javaEE的相关框架,自己就想研究源码,但是学了很久之后毫无头绪,所以还是扎扎实实学好Struts毕竟框架做起来要比自己写javaEE要简单,下面我们就来一步 ...
- 转载–移动互联网终端的touch事件,touchstart, touchend, touchmove
转载请注明: 转载自WEB前端开发(www.css119.com)-关注常见的WEB前端开发问题.最新的WEB前端开发技术(webApp开发.移动网站开发).最好的WEB前端开发工具和最全的WEB前端 ...
- HttpServletResponse对象(一)
web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象,和代表响应的response对象. request和response对象既然代表请求和响应,那么我 ...
- ThinkPHP框架基础
ThinkPHP 一.php框架基础介绍 真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维 ...
- go语法
背景 go语言算是比较常用的开发语言了,但是我发现自己在写代码的时候仍无法做到熟练掌握语法的程度,这个博客是我在因为语法不熟练而必须停下来的地方,整理下来方便查阅和记忆. 数组 ]int //arra ...
- 41个有关Python的小技巧【转】
内容来自网络整理 1. 拆箱 拆箱 2. 拆箱变量交换 拆箱变量交换 3. 扩展拆箱(只兼容python3) 扩展拆箱(只兼容python3) 4. 负数索引 负数索引 5. 负数索引 负数索引 ...
- drupal7的node的内容的存储位置
标题是存在node表中的,但是实际内容存在表field_data_body中
- html5实现滚动文字
<div class="custom-notice"> <i class="icon-notice"></i> <ma ...