RxSwift UI交互 - I

了解了RxSwift的基本概念和用法之后,我们通过一系列视频向大家介绍如何用RxSwift处理UI交互。在这个例子里,我们实现一个简单的登录UI,对比传统的delegate方式,我们将看到RxSwift在处理异步事件时的简洁和便利。

准备工作

下载项目初始模板。

首先,我们创建了一个Single View Application,并且安装好了RxSwift。在Main.storyboard里,我们添加两个UITextFile用于输入邮箱和密码,以及一个UIButton表示注册。

image

并且,我们在ViewController里,添加了对应的IBOutlet,以及一个用于回收Disposable对象的DisposeBag

class ViewController: UIViewController {

    @IBOutlet weak var email: UITextField!
    @IBOutlet weak var password: UITextField!
    @IBOutlet weak var register: UIButton!

    var bag: DisposeBag! = DisposeBag()

}

除此之外,我们还添加了一个辅助类InputValidator,它有两个类方法:

  • isValidEmail(email: String)用于验证email是否是一个合法的电子邮件:
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)用于验证密码的长度是否大于等于8;
class func isValidPassword(
    password: String) -> Bool {
    return password.characters.count >= 8
}

至此,所有的准备工作就结束了,接下来,我们来处理用户交互。


让输入框内容合法时变成绿色

先来处理Email的输入。

之前我们也提到过,RxSwift给UITextField添加了一个扩展rx_text,表示输入事件序列,而事件的值,是每一次输入后,UITextField中的字符串。因此,我们可以先使用map把字符串变成一个Bool,表示当前UITextField中的值是否是一个合法的电子邮件。

viewDidLoad方法里,添加下面的代码:

let emailObservable = 
    self.email.rx_text.map { 
        (input: String) -> Bool in
            return InputValidator.isValidEmail(input)
    }

然后,我们希望当内容为合法的Email时,给UITextField添加一个绿色的边框,因此,我们还要进一步把Observable变成一个Observable

emailObservable.map { (valid: Bool) -> UIColor in
    let color = valid ? 
        UIColor.greenColor() : UIColor.clearColor()

    return color
}

这样,我们就可以使用subscribeNext订阅这个事件了:

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)

这反而是最简单的一步,我们直接把.Next的associated value赋值给self.email.layer.borderColor属性就可以了。

最后,为了能看到这个绿色的边框,我们在viewDidLoad开始要设置一下边框的宽度:

self.email.layer.borderWidth = 1

这样,按Command + R编译执行,当我们输入一个完整的email后,就可以看到Email输入框变为绿色了:

image

接下来,我们可以用同样的方式处理password输入框,过程就不细说了,只是贴上代码:

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开始,也设置password输入框边框的宽度:

self.password.layer.borderWidth = 1

然后,Command + R编译执行,当我们在密码框中输入长度大于等于8的密码后,password输入框就变成绿色了:

image

这就是UITextField在RxSwift中的用法,简单来说,就是利用输入的字符串,把rx_text变换成我们需要的事件逻辑,然后订阅对应的事件进行操作就可以了

接下来,我们要实现另外一个效果:我们希望只有当Email和Password中的输入都合法时,才启用Sign Up按钮,否则禁用它,该怎么做呢?


禁用和启用UIButton

在我们的例子里,emailObservablepasswordObservable是两个独立的事件序列。如果我们要表达“它们的输入都合法”这样的语义,也就是说,这两个事件序列中最新的事件值都是合法的

为此,RxSwift提供了一个专门的operator,叫做combineLatest,它用于将多个事件序列中最新的事件进行合并。我们可以在这里找到combineLatest的详细定义。

至此,我们就有思路了。只要将emailObservablepasswordObservable中最新的事件进行合并,如果它们都是true,就启用Sign Up按钮,否则就禁用。

有了思路之后,就可以开工了。继续在viewDidLoad里,添加下面的代码:

Observable.combineLatest(
    emailObservable, passwordObservable) {
    (validEmail: Bool, validPassword: Bool) -> [Bool] in
    return [validEmail, validPassword]
}

combineLatest的前两个参数表示要合并的事件序列,第三个参数是一个closure,表示合并的方法,在我们的例子里,我们把emailObservable和passwordObservable中的两个最新事件的Bool,变成了一个Bool数组。

接下来,我们使用map把合并的结果变成一个单一的Observable

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
    }

这样,我们就得到了一个Observable,我们订阅它,然后设置按钮的状态就可以了:

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编译执行,就可以看到只有当Email和Password都输入正确后,Sign Up按钮才会被启用的效果了。

image

你可能感兴趣的:(RxSwift UI交互 - I)