了解了RxSwift的基本概念和用法之后,我們通過(guò)一系列視頻向大家介紹如何用RxSwift處理UI交互。在這個(gè)例子里,我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的登錄UI,對(duì)比傳統(tǒng)的delegate方式,我們將看到RxSwift在處理異步事件時(shí)的簡(jiǎn)潔和便利。
準(zhǔn)備工作
首先,我們創(chuàng)建了一個(gè)Single View Application,并且安裝好了RxSwift。在Main.storyboard里,我們添加兩個(gè)UITextFile用于輸入郵箱和密碼,以及一個(gè)UIButton表示注冊(cè)。

并且,我們?cè)赩iewController里,添加了對(duì)應(yīng)的IBOutlet,以及一個(gè)用于回收Disposable對(duì)象的DisposeBag:
class ViewController: UIViewController {
@IBOutlet weak var email: UITextField!
@IBOutlet weak var password: UITextField!
@IBOutlet weak var register: UIButton!
var bag: DisposeBag! = DisposeBag()
}
除此之外,我們還添加了一個(gè)輔助類InputValidator,它有兩個(gè)類方法:
-
isValidEmail(email: String)用于驗(yàn)證email是否是一個(gè)合法的電子郵件:
class func isValidEmail(email: String) -> Bool {
let re = try? NSRegularExpression(
pattern: "^\\S+@\\S+\\.\\S+$",
options: .CaseInsensitive)
if let re = re {
let range = NSMakeRange(0,
email.lengthOfBytesUsingEncoding(
NSUTF8StringEncoding))
let result = re.matchesInString(email,
options: .ReportProgress,
range: range)
return result.count > 0
}
return false
}
-
isValidPassword(password: String)用于驗(yàn)證密碼的長(zhǎng)度是否大于等于8;
class func isValidPassword(
password: String) -> Bool {
return password.characters.count >= 8
}
至此,所有的準(zhǔn)備工作就結(jié)束了,接下來(lái),我們來(lái)處理用戶交互。
讓輸入框內(nèi)容合法時(shí)變成綠色
先來(lái)處理Email的輸入。
之前我們也提到過(guò),RxSwift給UITextField添加了一個(gè)擴(kuò)展rx_text,表示輸入事件序列,而事件的值,是每一次輸入后,UITextField中的字符串。因此,我們可以先使用map把字符串變成一個(gè)Bool,表示當(dāng)前UITextField中的值是否是一個(gè)合法的電子郵件。
在viewDidLoad方法里,添加下面的代碼:
let emailObservable =
self.email.rx_text.map {
(input: String) -> Bool in
return InputValidator.isValidEmail(input)
}
然后,我們希望當(dāng)內(nèi)容為合法的Email時(shí),給UITextField添加一個(gè)綠色的邊框,因此,我們還要進(jìn)一步把Observable<Bool>變成一個(gè)Observable<UIColor>:
emailObservable.map { (valid: Bool) -> UIColor in
let color = valid ?
UIColor.greenColor() : UIColor.clearColor()
return color
}
這樣,我們就可以使用subscribeNext訂閱這個(gè)事件了:
emailObservable.map { (valid: Bool) -> UIColor in
let color = valid ?
UIColor.greenColor() : UIColor.clearColor()
return color
}.subscribeNext({
self.email.layer.borderColor = $0.CGColor
}).addDisposableTo(self.bag)
這反而是最簡(jiǎn)單的一步,我們直接把.Next的associated value賦值給self.email.layer.borderColor屬性就可以了。
最后,為了能看到這個(gè)綠色的邊框,我們?cè)?code>viewDidLoad開(kāi)始要設(shè)置一下邊框的寬度:
self.email.layer.borderWidth = 1
這樣,按Command + R編譯執(zhí)行,當(dāng)我們輸入一個(gè)完整的email后,就可以看到Email輸入框變?yōu)榫G色了:

接下來(lái),我們可以用同樣的方式處理password輸入框,過(guò)程就不細(xì)說(shuō)了,只是貼上代碼:
let passwordObservable =
self.password.rx_text.map {
(input: String) -> Bool in
return InputValidator.isValidPassword(input)
}
passwordObservable.map {
(valid: Bool) -> UIColor in
let color = valid ?
UIColor.greenColor() : UIColor.clearColor()
return color
}.subscribeNext({
self.password.layer.borderColor = $0.CGColor
}).addDisposableTo(self.bag)
最后,不要忘記在viewDidLoad開(kāi)始,也設(shè)置password輸入框邊框的寬度:
self.password.layer.borderWidth = 1
然后,Command + R編譯執(zhí)行,當(dāng)我們?cè)诿艽a框中輸入長(zhǎng)度大于等于8的密碼后,password輸入框就變成綠色了:

這就是UITextField在RxSwift中的用法,簡(jiǎn)單來(lái)說(shuō),就是利用輸入的字符串,把rx_text變換成我們需要的事件邏輯,然后訂閱對(duì)應(yīng)的事件進(jìn)行操作就可以了。
接下來(lái),我們要實(shí)現(xiàn)另外一個(gè)效果:我們希望只有當(dāng)Email和Password中的輸入都合法時(shí),才啟用Sign Up按鈕,否則禁用它,該怎么做呢?
禁用和啟用UIButton
在我們的例子里,emailObservable和passwordObservable是兩個(gè)獨(dú)立的事件序列。如果我們要表達(dá)“它們的輸入都合法”這樣的語(yǔ)義,也就是說(shuō),這兩個(gè)事件序列中最新的事件值都是合法的。
為此,RxSwift提供了一個(gè)專門的operator,叫做combineLatest,它用于將多個(gè)事件序列中最新的事件進(jìn)行合并。我們可以在這里找到combineLatest的詳細(xì)定義。
至此,我們就有思路了。只要將emailObservable和passwordObservable中最新的事件進(jìn)行合并,如果它們都是true,就啟用Sign Up按鈕,否則就禁用。
有了思路之后,就可以開(kāi)工了。繼續(xù)在viewDidLoad里,添加下面的代碼:
Observable.combineLatest(
emailObservable, passwordObservable) {
(validEmail: Bool, validPassword: Bool) -> [Bool] in
return [validEmail, validPassword]
}
combineLatest的前兩個(gè)參數(shù)表示要合并的事件序列,第三個(gè)參數(shù)是一個(gè)closure,表示合并的方法,在我們的例子里,我們把emailObservable和passwordObservable中的兩個(gè)最新事件的Bool,變成了一個(gè)Bool數(shù)組。
接下來(lái),我們使用map把合并的結(jié)果變成一個(gè)單一的Observable<Bool>:
Observable.combineLatest(
emailObservable, passwordObservable) {
(validEmail: Bool, validPassword: Bool) -> [Bool] in
return [validEmail, validPassword]
}
.map { (input: [Bool]) -> Bool in
let validValues = input.reduce(true,
combine: { $0 && $1 })
return validValues
}
這樣,我們就得到了一個(gè)Observable<Bool>,我們訂閱它,然后設(shè)置按鈕的狀態(tài)就可以了:
Observable.combineLatest(
emailObservable, passwordObservable) {
(validEmail: Bool, validPassword: Bool) -> [Bool] in
return [validEmail, validPassword]
}
.map { (input: [Bool]) -> Bool in
let validValues = input.reduce(true,
combine: { $0 && $1 })
return validValues
}
.subscribeNext { (isEnabled: Bool) in
self.register.enabled = isEnabled
}.addDisposableTo(self.bag)
然后,按Command + R編譯執(zhí)行,就可以看到只有當(dāng)Email和Password都輸入正確后,Sign Up按鈕才會(huì)被啟用的效果了。
