前言

看了前面的文章,相信很多同学还不知道RxSwift该怎么使用,这篇文件将带领大家一起写一个 注册登录(ps:本例子采用MVVM)的例子进行实战。本篇文章是基于RxSwift3.0写的,采用的是Carthage第三方管理工具导入的RxSwift3.0,关于Carthage的安装和使用,请参考Carthage的安装和使用

最终效果

下载Demo点我

前提准备

首先请大家新建一个swift工程,然后把RxSwift引入到项目中,然后能够编译成功就行。

然后我们来分析下各个界面的需求:

注册界面需求:

  • 输入用户名必须大于等于6个字符,不然密码不能输入;
  • 密码必须大于等于6个字符,不然重复密码不能输入;
  • 重复密码和密码必须一样, 不能注册按钮不能点击;
  • 点击注册按钮,提示注册成功或者注册失败;
  • 注册成功会写进本地的plist文件,然后输入用户名会检测该用户名是否已注册

登录界面需求:

  • 点击输入用户名,检测是否已存在,如果存在,户名可用,否则提示用户名不存在;
  • 输入密码,点击登录,如果密码错则提示密码错误,否则进入列表界面,提示登录成功。

列表界面需求:

  • 输入联系人的首字母进行筛选

好了,分析完上面的需求之后,是时候展示真正的技术了,let's go。

注册界面

大家现在storyboard中建立出下面这个样子的界面(ps:添加约束不在本篇范围内):

创建对应的文件

然后建立一个对应的控制器RegisterViewController类,另外创建一个RegisterViewModel.swift,将RegisterViewControllerstoryboard中的控制器关联,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类,最好不要继承NSObjectSwift中推荐尽量使用原生类。我们考虑到当文本框内容变化的时候,我们需要把文本框的内容当做参数传递进来进行处理,判断是否符合我们的要求,然后返回处理结果,也就是状态。基于此,我们创建一个Protocol.swift文件,创建一个enum用于表示我们处理结果,所以,我们在Protocol.swift文件中添加如下代码:

enum Result {
case ok(message:String)
case empty
case failed(message:String)
}

username处理

先写出总结:其实就是两个流的传递过程。

UI操作 -> ViewModel -> 改变数据

数据改变 -> ViewModel -> UI刷新

回到我们ServiceValidationService类中,写一个检测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.orEmptyRxCocoa库中的东西,它把TextFiledtext变成了一个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)
  • ViewModelusername处理结果usernameUseable绑定到nameLabel显示文案上,根据不同的结果显示不同的文案;
  • ViewModelusername处理结果usernameUseable绑定到pwdTextField,根据不同的结果判断是否可以输入。

关于上面的validationResultinputEnabled是需要我们自己去定制的,这就用到了RxSwift 系列(九) -- 那些难以理解的概念文章中的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结果,进行它的texttextColor显示
  • 我们对UITextField进行了UIBingObserver,根据result结果,对它的isEnabled进行设置。

写到这里,我们暂停一下,运行一下项目看下程序的运行情况,试着去输入username尝试一下效果,是不是很激动??

password处理

有了上面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()中初始化passwordUseablerePasswordUseable

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)

RxSwift 实战操作【注册登录】的更多相关文章

  1. 一步步开发自己的博客 .NET版(3、注册登录功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  2. Android开发案例 - 注册登录

    本文只涉及UI方面的内容, 如果您是希望了解非UI方面的访客, 请跳过此文. 在微博, 微信等App的注册登录过程中有这样的交互场景(如下图): 打开登录界面 在登录界面中, 点击注册, 跳转到注册界 ...

  3. 如何设计一个 App 的注册登录流程?

    移 动设备发力之前的登录方式很简单:用户名/邮箱+密码+确认密码,所有的用户登录注册都是围绕着邮箱来做.随着移动设备和社交网络的普及,邮箱不再是唯 一,渐渐的出现了微博,QQ,微信等第三方登录方式,手 ...

  4. Discuz! X2.5判断会员登录状态及外部调用注册登录框

    Discuz! X2.5判断会员登录状态及外部调用注册登录框 有关discuz论坛会员信息,收集的一些资料: 用dedecms+discuz做了个门户加论坛形式的网站,但是dedecms顶部目前只能q ...

  5. 解决stackoverflow打开慢不能注册登录

    http://blog.csdn.net/dream_an/article/details/50280977 解决stackoverflow打开慢不能注册登录 标签: stack overflowfi ...

  6. Node.js基于Express框架搭建一个简单的注册登录Web功能

    这个小应用使用到了node.js  bootstrap  express  以及数据库的操作 :使用mongoose对象模型来操作 mongodb 如果没了解过的可以先去基本了解一下相关概念~ 首先注 ...

  7. 原生js验证简洁美观注册登录页面

    序 一个以js验证表单的简洁的注册登录页面,不多说直接上图 效果 主要文件 完整代码 sign_up.html 注册表单 <!DOCTYPE html> <html lang=&qu ...

  8. JavaWeb笔记——注册登录系统项目思路

    功能:   > 注册   > 登录 --------------------------------- JSP:   * login.jsp  --> 登录表单   * regist ...

  9. php注册登录系统(一)-极简

    序 登录注册系统是日常上网最普通的操作,我设了一个分类一步步完善注册登录系统,若有哪里错误请慧教 所用语言:php 数据库 :mysql 本次实现功能: 1.用户注册 2.用户登录 主要文件: 完整代 ...

随机推荐

  1. 玩转nodeJS系列:使用cluster创建nodejs单机多核集群(多进程)

    前言: nodejs提供了cluster集群(支持端口共享的多进程),cluster基于child_process,process二次封装,方便我们使用该功能实现单机nodejs的web集群. 1.c ...

  2. [Lucene]-Lucene基本概述以及简单实例

    一.Lucene基本介绍: 基本信息:Lucene 是 Apache 软件基金会的一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎.Luc ...

  3. 初始化CSS

    为什么要初始化CSS? 建站老手都知道,这是为了考虑到浏览器的兼容问题,其实不同浏览器对有些标签的默认值是不同的,如果没对CSS初始化往往会出现浏览器之间的页面差异.当然,初始化样式会对SEO有一定的 ...

  4. mailto调用本地默认客户端发邮件

    下面介绍如何利用 Mailto功能: 实现 Mailto的基本html代码: <a href="mailto:123@qq.com">点击这里发邮件!</a> ...

  5. C++构造函数(一)

    本篇是介绍C++的构造函数的第一篇(共二篇),属于读书笔记,对C++进行一个系统的复习. 构造函数的概念和作用 全局变量未初始化时为0,局部变量未初始化时的值却是无法预测的.这是因为,全局变量的初始化 ...

  6. .Net中的AOP系列之《将AOP作为架构工具》

    返回<.Net中的AOP>系列学习总目录 本篇目录 编译时初始化和验证 编译时初始化 切面验证的正确用法 真实案例:复习线程 架构约束 强制架构 真实案例:NHibernate 多播 类级 ...

  7. JMS 之 Active MQ 消息存储

    一.消息的存储方式 ActiveMQ支持JMS规范中的持久化消息与非持久化消息 持久化消息通常用于不管是否消费者在线,它们都会保证消息会被消费者消费.当消息被确认消费后,会从存储中删除 非持久化消息通 ...

  8. CNN压缩:为反向传播添加mask(caffe代码修改)

    神经网络压缩的研究近三年十分热门,笔者查阅到相关的两篇博客,博主们非常奉献的提供了源代码,但是发发现在使用gpu训练添加mask的网络上,稍微有些不顺,特此再进行详细说明. 此文是在 基于Caffe的 ...

  9. CSS技巧和经验列表

    如何清除图片下方出现几像素的空白间隙? img{display:block;} 如何让文本垂直对齐文本输入框? input{vertical-align:middle;} 如何使文本溢出边界显示为省略 ...

  10. CYQ.Data V5 分布式自动化缓存设计介绍(二)

    前言: 最近一段时间,开始了<IT连>创业,所以精力和写的文章多数是在分享创业的过程. 而关于本人三大框架CYQ.Data.Aries.Taurus.MVC的相关文章,基本都很少写了. 但 ...