使用RXSwift撸一个简易的注册功能

408 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情


前言

RXSwift的功能十分强大,但学习曲线十分陡峭。不过呢先别怕,饭要一口一口的吃,路要一步一步的走,就先从撸一个注册功能开始吧。

咱们呢在开发的过程中,很多情况都是用原有思维去做一个功能,比如按钮添加事件使用@select、输入是否为空用string.length等,如果要用RXSwift,就需要去换一个思路,改变原有的写法。当然,这个也是需要时间去做的,这就是RXSwift学习成本高的原因之一。

我也是这段时间比较有空(工作量不饱和),抽空研究了一下RXSwift的基本功能,赶着刚好重构项目中注册登录,索性就用上了。中途也遇到了很多坑,不过也顺利上线了。

ps:这篇主要由本人的实际使用场景来编写,如有更好的方式,希望不吝赐教。

正片

需求

一般来说,app的注册功能有

  • 昵称、密码、确认密码(二次输入)、邮箱(可以没有)这些输入框。
  • 密码可以通过一个小按钮来控制显示和隐藏。
  • 没有输入完成不能点击确按钮
  • 等等

咱们就先实现这个些功能

实现

  1. 创建好ViewController,在里面添加上所需要的控件

定义账号输入框

private var accountTF : ZLYTextField = {
        var tf = ZLYTextField.init()
        tf.backgroundColor = thePro.colorBrgWhite
        tf.returnKeyType = .done
        tf.textColor = thePro.colorTextHigh
        tf.padding = UIEdgeInsets(top: 0, left: 22, bottom: 0, right: 22 )
        tf.maxWordNumber = maxUsernameLength
        tf.placeholder = HMLanguage(text: "用户名")
        tf.layer.borderWidth = _px
        tf.layer.borderColor = thePro.colorLine.cgColor
        tf.layer.cornerRadius = 5
        return tf
    }()

解释:这里的 ZLYTextField 是自定义的TextField,主要功能是可以增加输入框的内边距,可以用原生的代替。 下同

定义密码输入框 和密码隐藏显示按钮

 private var passwordTF_1 : ZLYTextField = {
        var tf = ZLYTextField.init()
        tf.backgroundColor = thePro.colorBrgWhite
        tf.returnKeyType = .done
        tf.textColor = thePro.colorTextHigh
        tf.padding = UIEdgeInsets(top: 0, left: 22, bottom: 0, right: 50 )
        tf.maxWordNumber = maxPasswordLength
        tf.placeholder = HMLanguage(text: "密码")
        tf.isSecureTextEntry = true
        tf.layer.borderWidth = _px
        tf.layer.borderColor = thePro.colorLine.cgColor
        tf.layer.cornerRadius = 5
        return tf
    }()
    
    lazy private var btnHidePwd_1 : UIButton = {
        var btn = UIButton.init(type: .custom)
        btn.setImage(UIImage.init(named: "login_icon_hide"), for: .normal)
        return btn
    }()
    
    private var passwordTF_2 : ZLYTextField = {
        var tf = ZLYTextField.init()
        tf.backgroundColor = thePro.colorBrgWhite
        tf.returnKeyType = .done
        tf.textColor = thePro.colorTextHigh
        tf.padding = UIEdgeInsets(top: 0, left: 22, bottom: 0, right: 50 )
        tf.maxWordNumber = maxPasswordLength
        tf.placeholder = HMLanguage(text: "确认密码")
        tf.isSecureTextEntry = true
        tf.layer.borderWidth = _px
        tf.layer.borderColor = thePro.colorLine.cgColor
        tf.layer.cornerRadius = 5
        return tf
    }()
    
    lazy private var btnHidePwd_2 : UIButton = {
        var btn = UIButton.init(type: .custom)
        btn.setImage(UIImage.init(named: "login_icon_hide"), for: .normal)
        return btn
    }()

解释:按钮的点击操作在初始化的时候可以不用写,因为要用RXSwift。

定义email输入框和确认按钮

private var emailTF : ZLYTextField = {
        var tf = ZLYTextField.init()
        tf.backgroundColor = thePro.colorBrgWhite
        tf.returnKeyType = .done
        tf.keyboardType = UIKeyboardType.emailAddress
        tf.textColor = thePro.colorTextHigh
        tf.padding = UIEdgeInsets(top: 0, left: 22, bottom: 0, right: 22 )
        tf.placeholder = HMLanguage(text: "邮箱")
        tf.layer.borderWidth = _px
        tf.layer.borderColor = thePro.colorLine.cgColor
        tf.layer.cornerRadius = 5
        return tf
    }()
    
    lazy private var confirmBtn : UIButton = {
        var btn = UIButton.init(type: .custom)
        btn.backgroundColor = UIColor.RGBA(180, g: 207, b: 250, a: 1)
        btn.isEnabled = false
        btn.setTitle(HMLanguage(text: "立即申请"), for: .normal)
        btn.setTitleColor(thePro.colorTextWhite, for: .normal)
        btn.setTitleColor(thePro.colorTextWhite, for: .disabled)
        btn.layer.cornerRadius = 4
        btn.layer.masksToBounds = true
        return btn
    }()

解释:这里同样不用对按钮进行点击操作的函数绑定,

2.使用RXSwift进行绑定操作。

首先,要在 viewDidLoad() 函数中初始化一个 var disposeBag = DisposeBag()

这个disposeBag 就相当于一根绳子,只要和这根绳子连接上的东西,在 ViewController被销毁时,都会被一并销毁。

然后我们来写绑定操作。

第一步:将密码的隐藏和显示与按钮绑定起来

    btnHidePwd_1.rx.tap.bind { [weak self] _ -> Void  in
            guard let ss = self else {return}
            let isHidePwd = ss.passwordTF_1.isSecureTextEntry
            let imgStr = isHidePwd ? "login_icon_view":"login_icon_hide"
            ss.btnHidePwd_1.setImage(UIImage.init(named: imgStr), for: .normal)
            ss.passwordTF_1.isSecureTextEntry = !isHidePwd
        }.disposed(by: disposeBag)
  
   btnHidePwd_2.rx.tap.bind { [weak self] _ -> Void  in
            guard let ss = self else {return}
            let isHidePwd = ss.passwordTF_2.isSecureTextEntry
            let imgStr = isHidePwd ? "login_icon_view":"login_icon_hide"
            ss.btnHidePwd_2.setImage(UIImage.init(named: imgStr), for: .normal)
            ss.passwordTF_2.isSecureTextEntry = !isHidePwd
        }.disposed(by: disposeBag)
        
        

解释:这里的rx.tap.bind 其实就是相当于这个按钮的点击操作,将原有的 #select(xxx()) 进行简化,将函数的使用和声明放在一起,容易理解(需要一定时间)。

第二步:将输入框是否为空和确认按钮是否能点击绑定起来

    let accountValid = accountTF.rx.text.orEmpty
            .map{$0.count >= minUsernameLength && $0.count <= maxUsernameLength}
            .share(replay: 1)
        
        let passwordValid_1 = passwordTF_1.rx.text.orEmpty
            .map { $0.count >= minPasswordLength && $0.count <= maxPasswordLength}
            .share(replay: 1)
        
        let passwordValid_2 = passwordTF_2.rx.text.orEmpty
            .map { $0.count >= minPasswordLength && $0.count <= maxPasswordLength}
            .share(replay: 1)
        
        
        let emailValid = emailTF.rx.text.orEmpty
            .map { $0.count >= 1}
            .share(replay: 1)
            

解释:这里呢,定义了4个条件accountValidpasswordValid_1passwordValid_2emailValid。当这4个条件都满足时,按钮才可以点击。

rx.text.orEmpty :表示这个输入框输入的值是否为空

.map{$0.count >= minUsernameLength && $0.count <= maxUsernameLength}:表示输入的值是否在最大最小长度之间

.share(replay: 1):表示共用一个源(这个不理解的话可以百度一下)。

让后将这些条件与按钮是否能点击进行绑定

Observable.combineLatest(passwordValid_1,passwordValid_2,accountValid,emailValid) { $0 && $1 && $2 && $3}
            .share(replay: 1)
            .subscribe(onNext: { [weak self] (bool) in
                self?.confirmBtn.isEnabled = bool // 修改按钮是否可以点击
                self?.confirmBtn.backgroundColor =  bool == true ?  thePro.colorMain :  UIColor.RGBA(180, g: 207, b: 250, a: 1)
            }, onError: nil, onCompleted: nil, onDisposed: nil)
            .disposed(by: disposeBag)

解释:Observable.combineLatest这个最初我也不太好理解,不过用久了就好说了,用最简单的说法就是 有一个函数叫Observable.combineLatest,它传入的参数是 条件1、条件2...(最多7个) 这个函数将这些条件组合起来进行判断。而怎么判断呢,就是{}里的内容。

根据我们所需要的功能,这些输入框都不能为空,所以我们需要在{}里全部进行&操作。这就是{ $0 && $1 && $2 && $3}的含义。

.subscribe:表示如果上面的条件成功后,就执行我这里的操作了。将按钮变为可以点击、改变按钮颜色等。

.disposed(by: disposeBag) : 表示绑在disposeBag上面

第三步:将点击事件和按钮进行绑定

confirmBtn.rx.tap
            .subscribe(onNext: { [weak self] _ in self?.verify() })
            .disposed(by: disposeBag)

这里要说明一下,rx.tap.subscribe()rx.tap.bind{}其实功能是一样的,都是当用户点击了按钮之后,可以去执行什么操作,如果这个操作的代码不多,可以直接bind,如果多的话,最好写个函数包着,使用subscribe()

结语

以上就是注册功能的关键代码,整体算下来,比用原有逻辑去写还是代码量少很多,有些判断可以直接合并处理,省去了许多if-else。总体来说简单使用还是很OK的