RxSwift UI交互 - I

了解了RxSwift的基本概念和用法之后,我們通過(guò)一系列視頻向大家介紹如何用RxSwift處理UI交互。在這個(gè)例子里,我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的登錄UI,對(duì)比傳統(tǒng)的delegate方式,我們將看到RxSwift在處理異步事件時(shí)的簡(jiǎn)潔和便利。

準(zhǔn)備工作

下載項(xiàng)目初始模板。

首先,我們創(chuàng)建了一個(gè)Single View Application,并且安裝好了RxSwift。在Main.storyboard里,我們添加兩個(gè)UITextFile用于輸入郵箱和密碼,以及一個(gè)UIButton表示注冊(cè)。

image

并且,我們?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色了:

image

接下來(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輸入框就變成綠色了:

image

這就是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

在我們的例子里,emailObservablepasswordObservable是兩個(gè)獨(dú)立的事件序列。如果我們要表達(dá)“它們的輸入都合法”這樣的語(yǔ)義,也就是說(shuō),這兩個(gè)事件序列中最新的事件值都是合法的。

為此,RxSwift提供了一個(gè)專門的operator,叫做combineLatest,它用于將多個(gè)事件序列中最新的事件進(jìn)行合并。我們可以在這里找到combineLatest的詳細(xì)定義。

至此,我們就有思路了。只要將emailObservablepasswordObservable中最新的事件進(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ì)被啟用的效果了。

image

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容