零基础也能看懂的 RxSwift 官方 Example 解析(1) Bindings

概述

很多小伙伴在初学 RxSwift 时,在面对大量的操作符和各种抽象的概念时可能都会感到无从下手,但其实官方提供的 Example 就能够很方便的帮助我们学习 RxSwift 的各种概念以及如何与 MVVM 相结合。毕竟直接看实际的运用场景比看抽象的概念要容易理解的多。

我个人认为 RxSwift 的核心其实很简单,就是把要操作的事件、属性都抽象成一个个Observable,剩下的变换、组合、过滤、响应(订阅)、线程调度、生命周期管理都是你对 Observable 的加工和处理。如果你真的理解了什么是 Observable,哪些东西可以被包装成 Observable 那么你已经成功了一半啦,就像你刚学面向对象编程,搞懂了什么是对象,哪些东西可以被抽象成对象以后,后面的学习是不是顺利了很多呢。

其实初学的时候真的不用在面对大量的操作符时困惑,也不要有怕难的心态,觉得这么多东西我得记到什么时候。可以先看一遍文档有能力的也可以看看源码。用的多了你会发现和我们平常写代码一样,常用的 Api 就那些,其他用的少的真忘记的时候再翻翻文档就好。(中文文档 RxSwift-Chinese-Documentation)

好了废话不多说,本篇所有示例代码在 RxSwift 官方项目中就有,下载完了直接运行即可。

可以看到官方为我们分了三块内容,

  • iPhone Example
  • TableView 和 CollectionView Example
  • 综合的 Example

本篇文章我们先从这些 Demo 里最简单的 Bindings 说起。

Adding numbers

先看运行效果

有三个 UITextField 任何一个 UITextField 输入内容时,把三者相加,在最底部 UILabel 显示结果。

这是一个很简单的响应式编程的例子,这个页面主要由四个元素组成

@IBOutlet weak var number1: UITextField!
@IBOutlet weak var number2: UITextField!
@IBOutlet weak var number3: UITextField!
@IBOutlet weak var result: UILabel!

先考虑一下以往我们会怎么实现:

  1. 监听三个 UITextField 的文本框改变(DelegateKVONSNotificationCenteraddTarget)。
  2. 在回调方法中将三个 UITextField 输入内容转成 Int 相加并赋值给 UILabel

原生实现最大的问题在于,无论哪种方式,监听文本框内容改变的代码都不是那么的优雅。

看一下用 RxSwift 如何实现。回想我们刚刚所说的核心,把属性或者事件抽象成 Observable,再对 Observable 进行操作。

  1. 包装 (把三个 UITextField 输入的内容抽象成三个 Observable)
    number1.rx.text.orEmpty
    number2.rx.text.orEmpty
    number3.rx.text.orEmpty
    
  2. 组合 (把三个 Observable 组合,并将每一个值转成 Int 类型相加)。
    Observable.combineLatest(number1.rx.text.orEmpty, number2.rx.text.orEmpty, number3.rx.text.orEmpty) { textValue1, textValue2, textValue3 -> Int in
     return (Int(textValue1) ?? 0) + (Int(textValue2) ?? 0) + (Int(textValue3) ?? 0)
    }
    
    文档中为我们描述的 combineLatest 的作用。

    当多个 Observables 中任何一个发出一个元素,就发出一个元素。这个元素是由这些 Observables 中最新的元素,通过一个函数组合起来的。

说的再直白一点就是,这三个 UITextField 任意一个有新值的时候都会被组合成新的Observable

  1. 转换 (把 Int 类型转换成 String 类型)
    .map { $0.description }
    
  2. 响应 (把值绑定到 UILabel)
    .bind(to: result.rx.text)
    
  3. 管理生命周期
    .disposed(by: disposeBag)
    
    怎么样是不是很简单呢,只需要6行代码我们就完成了这个需求。

SimpleValidation

运行效果

  • 当用户输入用户名时,如果用户名不足 5 个字就显示红色提示语,并且无法输入密码,当用户名符合要求时才可以输入密码。
  • 同样的当用户输入的密码不到 5 个字时也显示红色提示语。
  • 当用户名和密码有一个不符合要求时底部的绿色按钮不可点击,只有当用户名和密码同时有效时按钮才可点击。
  • 当点击绿色按钮后弹出一个提示框

页面由以下元素组成

/// 用户名
@IBOutlet weak var usernameOutlet: UITextField!
/// 用户名红色提示语
@IBOutlet weak var usernameValidOutlet: UILabel!

/// 密码
@IBOutlet weak var passwordOutlet: UITextField!
/// 密码红色提示语
@IBOutlet weak var passwordValidOutlet: UILabel!
/// 按钮
@IBOutlet weak var doSomethingOutlet: UIButton!

还是先考虑一下以往我们会怎么实现:

  1. 监听两个 UITextField 的文本框改变(DelegateKVONSNotificationCenteraddTarget)。
  2. 在回调方法中判断用户名是否符合要求,用判断结果去设置提示语是否隐藏和密码框是否可以输入。
  3. 在回调方法中判断密码是否符合要求,用判断结果去设置提示语是否隐藏。
  4. 拿到前两个判断的结果,当前两个的结果都为真时,按钮可以点击。
  5. 添加按钮点击事件。

看一下用 RxSwift 如何实现。还是我们刚刚所说的核心,把属性或者事件抽象成 Observable,再对 Observable 进行操作。

  1. 用户名格式是否正确 (变换)
    let usernameValid = usernameOutlet.rx.text.orEmpty
    .map { $0.count >= minimalUsernameLength }
    .share(replay: 1)
    
  2. 密码格式是否正确 (变换)
    let passwordValid = passwordOutlet.rx.text.orEmpty
    .map { $0.count >= minimalPasswordLength }
    .share(replay: 1)
    
  3. 两者都正确(组合 ,combineLatest 作用上面介绍过,再巩固一下)
    let everythingValid = Observable.combineLatest(usernameValid, passwordValid) { $0 && $1 }
    .share(replay: 1)
    
    这里你可能比较疑惑这个 .share(replay: 1) 是干嘛的。

通俗一点说,当有多个订阅者去订阅同一个 Observable 的时候,我们不希望 Observable 每次有新的订阅者都去执行。

  1. 响应
usernameValid
.bind(to: passwordOutlet.rx.isEnabled)
.disposed(by: disposeBag)
usernameValid
.bind(to: usernameValidOutlet.rx.isHidden)
.disposed(by: disposeBag)
passwordValid
.bind(to: passwordValidOutlet.rx.isHidden)
.disposed(by: disposeBag)
everythingValid
.bind(to: doSomethingOutlet.rx.isEnabled)
.disposed(by: disposeBag)

由于 .share(replay: 1) 的存在,这个判断密码或者用户名是否合法的操作,不管以后被多少人订阅,只会判断一次,这样可以避免不必要的资源消耗。

  1. 按钮添加点击事件
    doSomethingOutlet.rx.tap
    .subscribe(onNext: { [weak self] _ in self?.showAlert() })
    .disposed(by: disposeBag)
    
    当然这步我们是可以通过扩展的方式让他更加优雅的,这篇我们不做过多讲解。

最后说句题外话,学东西还是要从简单的学起,这样会多一些正面积极的反馈,就像是打游戏总会一段时间就有一个小的奖励,刺激你继续玩下去。如果一上来就看大量的操作符,各种概念和源码,挫败感一大就不想学了。

下一篇将更新如何与 MVVM 结合做一个 GitHub 的注册界面。


 上一篇
用 RxSwift 为 Controller 瘦身(1),优雅的使用网络请求(Moya) + 数据缓存(Cache) 用 RxSwift 为 Controller 瘦身(1),优雅的使用网络请求(Moya) + 数据缓存(Cache)
概述 View Controller 向来是 MVC (Model-View-View Controller) 中最让人头疼的一环,MVC 架构本身并不复杂,但开发者很容易将大量代码扔到用于协调 View 和 Model 的 Control
2019-02-13
下一篇 
在 iOS 上实现基于协议的 MVP 在 iOS 上实现基于协议的 MVP
概述如果你做过 Android 开发,那你一定知道,MVP 是 Google 官方推荐的 Android 开发架构。和 iOS 一样,Android 也存在着如果代码不够规范导致 C 层(Activity)过于臃肿的问题。 对于 MVP ,
2019-02-13