持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
RXSwift的功能十分强大,但学习曲线十分陡峭。不过呢先别怕,饭要一口一口的吃,路要一步一步的走,就先从撸一个注册功能开始吧。
咱们呢在开发的过程中,很多情况都是用原有思维去做一个功能,比如按钮添加事件使用@select、输入是否为空用string.length等,如果要用RXSwift,就需要去换一个思路,改变原有的写法。当然,这个也是需要时间去做的,这就是RXSwift学习成本高的原因之一。
我也是这段时间比较有空(工作量不饱和),抽空研究了一下RXSwift的基本功能,赶着刚好重构项目中注册登录,索性就用上了。中途也遇到了很多坑,不过也顺利上线了。
ps:这篇主要由本人的实际使用场景来编写,如有更好的方式,希望不吝赐教。
正片
需求
一般来说,app的注册功能有
- 昵称、密码、确认密码(二次输入)、邮箱(可以没有)这些输入框。
- 密码可以通过一个小按钮来控制显示和隐藏。
- 没有输入完成不能点击确按钮
- 等等
咱们就先实现这个些功能
实现
- 创建好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个条件accountValid、passwordValid_1、passwordValid_2、emailValid。当这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的