看了前面的文章,相信很多同学还不知道
该怎么使用,这篇文件将带领大家一起写一个 注册登录(ps: 本例子采用
- RxSwift
)的例子进行实战。本篇文章是基于
- MVVM
写的,采用的是
- RxSwift3.0
第三方管理工具导入的
- Carthage
,关于
- RxSwift3.0
的安装和使用,请参考 Carthage 的安装和使用。
- Carthage
下载 Demo 点我
首先请大家新建一个
工程,然后把
- swift
引入到项目中,然后能够编译成功就行。
- RxSwift
然后我们来分析下各个界面的需求:
好了,分析完上面的需求之后,是时候展示真正的技术了,let's go。
大家现在
中建立出下面这个样子的界面 (ps: 添加约束不在本篇范围内):
- storyboard
然后建立一个对应的控制器
类,另外创建一个
- RegisterViewController
,将
- RegisterViewModel.swift
与
- RegisterViewController
中的控制器关联,
- storyboard
看起来应该是这样子的:
- RegisterViewController
- class RegisterViewController: UIViewController {@IBOutlet weak
- var userNameTextField: UITextField ! @IBOutlet weak
- var nameLabel: UILabel !
- @IBOutlet weak
- var pwdTextField: UITextField ! @IBOutlet weak
- var pwdLabel: UILabel !
- @IBOutlet weak
- var rePwdTextField: UITextField ! @IBOutlet weak
- var rePwdLabel: UILabel !
- @IBOutlet weak
- var registButton: UIButton ! @IBOutlet weak
- var loginButton: UIBarButtonItem !
- override func viewDidLoad() {
- super.viewDidLoad()
- }
- override func didReceiveMemoryWarning() {
- super.didReceiveMemoryWarning()
- // Dispose of any resources that can be recreated.
- }
- }
另外,我们创建一个
文件。
- Service.swift
文件主要负责一些网络请求,和一些数据访问的操作。然后供
- Service
使用,由于本次实战没有使用到网络,所以我们只是模拟从本地
- ViewModel
文件中读取用户数据。
- plist
首先我们在
文件中创建一个
- Service
类,最好不要继承
- ValidationService
,
- NSObject
中推荐尽量使用原生类。我们考虑到当文本框内容变化的时候,我们需要把文本框的内容当做参数传递进来进行处理,判断是否符合我们的要求,然后返回处理结果,也就是状态。基于此,我们创建一个
- Swift
文件,创建一个
- Protocol.swift
用于表示我们处理结果,所以,我们在
- enum
文件中添加如下代码:
- Protocol.swift
- enum Result {
- case ok(message:
- String)
- case empty
- case failed(message:
- String)
- }
先写出总结:其实就是两个流的传递过程。
UI 操作 -> ViewModel -> 改变数据
数据改变 -> ViewModel -> UI 刷新
回到我们
中
- Service
类中,写一个检测
- ValidationService
的方法。它看起来应该是这个样子的:
- username
- class ValidationService {
- // 单例类
- static let instance = ValidationService() private init() {}
- let minCharactersCount = 6
- func validationUserName(_ name: String) - >Observable < Result > {
- if name.characters.count == 0 { // 当字符串为空的时候,什么也不做
- return Observable.just(Result.empty)
- }
- if name.characters.count < minCharactersCount {
- return Observable.just(Result.failed(message: "用户名长度至少为6位"))
- }
- if checkHasUserName(name) {
- return Observable.just(Result.failed(message: "用户名已存在"))
- }
- return Observable.just(Result.ok(message: "用户名可用"))
- }
- func checkHasUserName(_ userName: String) - >Bool {
- let filePath = NSHomeDirectory() + "/Documents/users.plist"guard let userDict = NSDictionary(contentsOfFile: filePath)
- else {
- return false
- }
- let usernameArray = userDict.allKeys as NSArray
- return usernameArray.contains(userName)
- }
- }
接下来该处理我们的
了,我们声明一个
- RegisterViewModel
,指定为
- username
类型,为什么是一个
- Variable
类型?因为它既是一个
- Variable
,又是一个
- Observer
,所以我们声明它是一个
- Observable
类型的对象。我们对
- Variable
处理应该会有一个结果,这个结果应该是由界面监听来改变界面显示,因此我们声明一个
- username
表示对
- usernameUseable
处理的一个结果,因为它是一个
- username
,所以我们将它声明为
- Observable
类型的对象,所以
- Observable
看起来应该是这样子的:
- RegisterViewModel
- class RegisterViewModel {
- let username = Variable < String > ("")
- let usernameUseable: Observable < Result >
- init() {}
- }
然后我们再写
,它看起来应该是这样子的:
- RegisterViewController
- private let disposeBag = DisposeBag()
- override func viewDidLoad() {
- super.viewDidLoad()
- let viewModel = RegisterViewModel()
- userNameTextField.rx.text.orEmpty.bind(to: viewModel.username).disposed(by: disposeBag)
- }
是
- userNameTextField.rx.text.orEmpty
库中的东西,它把
- RxCocoa
的
- TextFiled
变成了一个
- text
,后面的
- Observable
我们可以
- orEmpty
点进去看下,它会把
- Command
过滤
- String?
帮我们变为
- nil
类型。
- String
的意思是
- bind(to:viewModel.username)
作为一个
- viewModel.username
(观察者) 观察
- observer
上的内容变化。
- userNameTextField
来盛放我们这些监听的资源。
- disposeBag
现在,回到我们的
中,我们添加如下代码:
- RegisterViewModel
- init() {
- let service = ValidationService.instance
- usernameUseable = username.asObservable().flatMapLatest {
- username in
- return service.validationUserName(username).observeOn(MainScheduler.instance).catchErrorJustReturn(.failed(message: "userName检测出错")).shareReplay(1)
- }
- }
中,我们把
- viewModel
当做
- username
(被观察者),然后对里面的元素进行处理之后发射对应的事件。
- observable
下面我们在
中处理我们的
- RegisterViewController
请求结果。我们在
- username
中添加下列代码:
- ViewDidLoad
- viewModel.usernameUseable.bind(to: nameLabel.rx.validationResult).addDisposableTo(disposeBag)
- viewModel.usernameUseable.bind(to: pwdTextField.rx.inputEnabled).addDisposableTo(disposeBag)
中
- ViewModel
处理结果
- username
绑定到
- usernameUseable
显示文案上,根据不同的结果显示不同的文案;
- nameLabel
中
- ViewModel
处理结果
- username
绑定到
- usernameUseable
,根据不同的结果判断是否可以输入。
- pwdTextField
关于上面的
和
- validationResult
是需要我们自己去定制的,这就用到了 RxSwift 系列 (九) -- 那些难以理解的概念文章中的
- inputEnabled
了。
- UIBindingObserver
所以,我们在
文件中添加如下代码:
- Protocol.swift
- extension Result {
- var isValid: Bool {
- switch self {
- case.ok:
- return true
- default:
- return false
- }
- }
- }
- extension Result {
- var textColor:
- UIColor {
- switch self {
- case.ok:
- return UIColor(red:
- 138.0 / 255.0, green: 221.0 / 255.0, blue: 109.0 / 255.0, alpha: 1.0)
- case.empty:
- return UIColor.black
- case.failed:
- return UIColor.red
- }
- }
- }
- extension Result {
- var description:
- String {
- switch self {
- case let.ok(message):
- return message
- case.empty:
- return ""
- case let.failed(message):
- return message
- }
- }
- }
- extension Reactive where Base:
- UILabel {
- var validationResult:
- UIBindingObserver < Base,
- Result > {
- return UIBindingObserver(UIElement: base) {
- label,
- result in label.textColor = result.textColor label.text = result.description
- }
- }
- }
- extension Reactive where Base: UITextField {
- var inputEnabled: UIBindingObserver < Base,
- Result > {
- return UIBindingObserver(UIElement: base) {
- textFiled,
- result in textFiled.isEnabled = result.isValid
- }
- }
- }
进行了扩展,添加了
- Result
属性,如果状态是
- isValid
,这个属性就为
- ok
,否则为
- true
- false
添加了一个
- Result
属性,如果状态为
- textColor
则为绿色,否则使用红色
- ok
进行了
- UILabel
,根据
- UIBingObserver
结果,进行它的
- result
和
- text
显示
- textColor
进行了
- UITextField
,根据
- UIBingObserver
结果,对它的
- result
进行设置。
- isEnabled
写到这里,我们暂停一下,运行一下项目看下程序的运行情况,试着去输入
尝试一下效果,是不是很激动??
- username
有了上面
的理解,相信大家对
- username
也就熟门熟路了,因此有些细节就不做描述了。
- password
我们现在对
中添加对
- Service
的处理:
- password
- func validationPassword(_ password: String) - >Result {
- if password.characters.count == 0 {
- return Result.empty
- }
- if password.characters.count < minCharactersCount {
- return.failed(message: "密码长度至少为6位")
- }
- return.ok(message: "密码可用")
- }
- func validationRePassword(_ password: String, _ rePassword: String) - >Result {
- if rePassword.characters.count == 0 {
- return.empty
- }
- if rePassword.characters.count < minCharactersCount {
- return.failed(message: "密码长度至少为6位")
- }
- if rePassword == password {
- return.ok(message: "密码可用")
- }
- return.failed(message: "两次密码不一样")
- }
处理我们输入的密码;
- validationPassword
处理我们输入的重复密码;
- validationRePassword
类型的值,因为我们外面不需要对这个过程进行监听,所以不必返回一个新的序列。
- Result
在
中添加需要的
- RegisterViewModel
:
- observable
- let password = Variable < String > ("") let rePassword = Variable < String > ("")
- let passwordUseable: Observable < Result > let rePasswordUseable: Observable < Result >
然后在
中初始化
- init()
和
- passwordUseable
:
- rePasswordUseable
- passwordUseable = password.asObservable().map {
- passWord in
- return service.validationPassword(passWord)
- }.shareReplay(1)
- rePasswordUseable = Observable.combineLatest(password.asObservable(), rePassword.asObservable()) {
- return service.validationRePassword($0, $1)
- }.shareReplay(1)
回到
中,添加对应的绑定:
- RegisterViewController
- pwdTextField.rx.text.orEmpty.bind(to: viewModel.password).disposed(by: disposeBag)
- rePwdTextField.rx.text.orEmpty.bind(to: viewModel.rePassword).disposed(by: disposeBag)
- viewModel.passwordUseable.bind(to: pwdLabel.rx.validationResult).addDisposableTo(disposeBag)
- viewModel.passwordUseable.bind(to: rePwdTextField.rx.inputEnabled).addDisposableTo(disposeBag)
- viewModel.rePasswordUseable.bind(to: rePwdLabel.rx.validationResult).addDisposableTo(disposeBag)
来源: http://www.cnblogs.com/scott-mr/p/7240991.html