项目中学习ReactiveCocoa的使用方法
一、注册控制器
控制器上的一个属性
@property (weak, nonatomic) IBOutlet UIBarButtonItem *signInBtn;
在
viewDidLoad
方法中写入
self.signInBtn.rac_command = self.viewModel.executeSignInCommand;
self.viewModel是控制器上的一个viewModel
@property (nonatomic, weak) MSFAuthorizeViewModel *viewModel;
其上有个属性
@property (nonatomic, strong) RACCommand *executeSignInCommand;
这个executeSignInCommand在viewModel的初始化方法中生成
- (instancetype)initWithServices:(id <MSFViewModelServices>)services { self = [super init];
if (!self) {
return nil;
}
_services = services; ..... _executeSignInCommand = [[RACCommand alloc]
initWithSignalBlock:^RACSignal *(id input) { self.loginType = MSFLoginSignIn;
[self.services presentViewModel:self];
return [RACSignal return:nil];
}];
....
}
而presentViewModel:仅仅只是一个协议,没有实现,有个样本
- (void)presentViewModel:(id)viewModel {
id viewController; if ([viewModel isKindOfClass:MSFAuthorizeViewModel.class]) {
MSFAuthenticateViewController *loginViewController = [[MSFAuthenticateViewController alloc] initWithViewModel:viewModel];
viewController = [[UINavigationController alloc] initWithRootViewController:loginViewController];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(, , CGRectGetWidth(UIScreen.mainScreen.bounds), )];
view.backgroundColor = UIColor.blackColor;
[[(UIViewController *)viewController view] addSubview:view];
} else {
NSLog(@"an unknown ViewModel was present!");
} [self.navigationController presentViewController:viewController animated:YES completion:nil];
}
再看看控制器的其他属性
@property (nonatomic, weak) IBOutlet UITextField *usernameF;
@property (nonatomic, weak) IBOutlet UITextField *captchaF;
@property (nonatomic, weak) IBOutlet UITextField *passwordF;
也是在viewDidLoad中处理的,方法viewWillAppear有一执行就会有一个信号发送,控制器订阅了这个信号,并在收到信号后,执行操作:
把控件上的文件赋值给viewModel上的对应的属性。
@weakify(self)
[[self rac_signalForSelector:@selector(viewWillAppear:)] subscribeNext:^(id x) {
@strongify(self)
self.viewModel.username = self.usernameF.text;
self.viewModel.password = self.passwordF.text;
self.viewModel.loginType = MSFLoginSignUp;
}];
当然,相应的textField文本改变时,我们也会处理
输入的文本长度超过规定的长度,则进行截取然后放置到field中,再赋值给viewModel中对应的属性
[self.username.rac_textSignal subscribeNext:^(id x) {
@strongify(self)
if ([x length] > MSFAuthorizeUsernameMaxLength) self.username.text = [x substringToIndex:MSFAuthorizeUsernameMaxLength];
self.viewModel.username = self.username.text;
}];
密码输入框也是
[self.password.rac_textSignal subscribeNext:^(id x) {
@strongify(self)
if ([x length] > MSFAuthorizePasswordMaxLength) self.password.text = [x substringToIndex:MSFAuthorizePasswordMaxLength];
self.viewModel.password = self.password.text;
}];
还有密码field在按下下一步的return键时,就相当于按下了注册按钮,此时就会调用viewModel上的executeSignUp命令
[self.password.rac_keyboardReturnSignal subscribeNext:^(id x) {
@strongify(self)
[self.viewModel.executeSignUp execute:nil];
}];
这个命令也是跟登录命令一样在同一个方法中初始化
_executeSignUp = [[RACCommand alloc] initWithEnabled:self.signUpValidSignal
signalBlock:^RACSignal *(id input) {
@strongify(self) return [self executeSignUpSignal];
}];
当然,这个命令要受到能点不能点击的影响self.signUpValidSignal,当用户名和密码还有验证码都有输入的情况下,就会调用自身上的一个excuteSignUpSignal
- (RACSignal *)signUpValidSignal {
return [RACSignal
combineLatest:@[
RACObserve(self, username),
RACObserve(self, password),
RACObserve(self, captcha),
]
reduce:^id(NSString *username, NSString *password, NSString *captcha){
return @(username != nil && password != nil && captcha != nil);
}];
}
突然间感觉这里好复杂不再深究,先看其他的
- (RACSignal *)executeSignUpSignal {
if (![self.username isMobile]) {
return [RACSignal error:[self.class errorWithFailureReason:@"请填写真实的手机号码"]];
} else if (![self.password isPassword]) {
return [RACSignal error:[self.class errorWithFailureReason:@"请填写8到16位数字和字母组合的密码"]];
} else if (![self.captcha isCaptcha]) {
return [RACSignal error:[self.class errorWithFailureReason:@"请填写验证码"]];
} else if (!self.agreeOnLicense) {
return [RACSignal error:[self.class errorWithFailureReason:@"请阅读注册协议"]];
} MSFUser *user = [MSFUser userWithServer:MSFServer.dotComServer];
return [[MSFClient
signUpAsUser:user password:self.password phone:self.username captcha:self.captcha]
doNext:^(MSFClient *client) {
_signInValid = YES;
[self.services setHttpClient:client];
[[client fetchUserInfo] subscribeNext:^(MSFUser *x) {
[client.user mergeValueForKey:@keypath(x.personal) fromModel:x];
[client.user mergeValueForKey:@keypath(x.professional) fromModel:x];
[client.user mergeValueForKey:@keypath(x.contacts) fromModel:x];
[client.user mergeValueForKey:@keypath(x.profiles) fromModel:x];
[client.user mergeValueForKey:@keypath(x.insurance) fromModel:x];
}];
}];
}
有个显示密码与隐藏密码的按钮
[[self.showPasswordButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
@strongify(self)
self.showPasswordButton.selected = !self.showPasswordButton.selected;
NSString *text = self.password.text;
self.password.text = text;
self.password.enabled = NO;
[self.password setSecureTextEntry:!self.showPasswordButton.selected];
self.password.enabled = YES;
[self.password becomeFirstResponder];
}];
再看看验证码输入框,长度限制、截取、同步viewModel的对应属性
[self.captcha.rac_textSignal subscribeNext:^(id x) {
@strongify(self)
if ([x length] > MSFAuthorizeCaptchaMaxLength) self.captcha.text = [x substringToIndex:MSFAuthorizeCaptchaMaxLength];
self.viewModel.captcha = self.captcha.text;
}];
同步viewModel上的命令
self.sendCaptchaButton.rac_command = self.viewModel.executeCaptcha;
执行命令时的操作,键盘释放,SVProgressHUD显示正在获取验证码
[self.sendCaptchaButton.rac_command.executionSignals subscribeNext:^(RACSignal *captchaSignal) {
@strongify(self)
[self.view endEditing:YES];
[SVProgressHUD showWithStatus:@"正在获取验证码" maskType:SVProgressHUDMaskTypeClear];
[captchaSignal subscribeNext:^(id x) {
[SVProgressHUD dismiss];
}];
}];
当这个命令有错误时的回调
[self.sendCaptchaButton.rac_command.errors subscribeNext:^(NSError *error) {
[SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedFailureReasonErrorKey]];
}];
同理,提交、注册按钮也是类似 的,先绑定命令
self.commitButton.rac_command = self.viewModel.executeSignUp;
然后,执行命令的回调,释放键盘,提示正在注册...然后会发送一个通知,没看到这个是什么意思 [signUpSignal subscribeNext:^(id x) {
[self.commitButton.rac_command.executionSignals subscribeNext:^(RACSignal *signUpSignal) {
@strongify(self)
[self.view endEditing:YES];
[SVProgressHUD showWithStatus:@"正在注册..." maskType:SVProgressHUDMaskTypeClear];
[signUpSignal subscribeNext:^(id x) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"MSFREQUESTCONTRACTSNOTIFACATION" object:nil];
[SVProgressHUD dismiss];
}];
}];
收到错误时的信号
[self.commitButton.rac_command.errors subscribeNext:^(NSError *error) {
[SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedFailureReasonErrorKey]];
}];
验证码倒计时Label
@property (nonatomic, weak) IBOutlet UILabel *counterLabel;
viewDidLoad中操作,viewModel上的对应的属性变更时,会同步到这个控制器上的label来
RAC(self, counterLabel.text) = RACObserve(self, viewModel.counter);
项目中学习ReactiveCocoa的使用方法的更多相关文章
- SuperDiamond在JAVA项目中的三种应用方法实践总结
SuperDiamond在JAVA项目中的三种应用方法实践总结 1.直接读取如下: @Test public static void test_simple(){ PropertiesConfigur ...
- swift项目中使用OC/C的方法
假如有个OC类OCViewController : UIViewController类里有两个方法 //swift调用oc或c的混编是比较常用的,反过来的调用很少.这里只写了swift调用oc和c的方 ...
- web项目中获取各种路径的方法
~Apple web项目中各种路径的获取 1.可以在servlet的init方法里 String path = getServletContext().getRealPath("/&qu ...
- 【从业余项目中学习2】C# 实现调用Matlab函数(Visual Studio:2008, Matlab:R2009a)
最近正在给客户做的个人项目,要求实现C#与Matlab之间的调用,即C# winform界面收集用户输入的参数,将参数传递给Matlab的算法计算,Matlab函数返回的结果显示在winform界面上 ...
- angular6项目中使用echarts图表的方法(有一个坑,引用报错)
1.安装相关依赖(采用的webpack) npm install ecahrts --save npm install ngx-echarts --save 2.angular.json 配置echa ...
- 【从业余项目中学习1】C# 实现XML存储用户名密码(MD5加密)
最近在写一个C#的项目,用户需求是实现Winform的多文档界面与Matlab算法程序之间的交互.做了一段时间发现,这既能利用业余时间,实战中也可学习一些技术,同时刚毕业也增加一份收入.所以后面会不断 ...
- 从项目中学习HTML+CSS
最近由于工作原因以及自己的懈怠,已经很久都没有更新过博客了.通过这段时间,我发现坚持一件事情是真的很难,都说万事开头难,但是在放弃这件事上好像开头了后面就顺理成章的继续下去了.中间即使不怎么情愿也在努 ...
- [冲昏头脑]IDEA中的maven项目中学习log4j的日志操作
第一,你要有log4j的对应的包,由于我用的maven,所以直接在pom.xml文件依赖下载则可,如你尚为有此包,请自行百度下载导入,或上http://www.mvnrepository.com/搜索 ...
- 在项目中添加ReactiveCocoa #安装与配置
这是对官方教程的补充 To add RAC to your application: Add the ReactiveCocoa repository as a submodule of your a ...
随机推荐
- 2019牛客暑期多校训练营(第九场)The power of Fibonacci——循环节&&CRT
题意 求 $\displaystyle \sum_{i=1}^n F_i^m $,($1 \leq n\leq 10^9,1 \leq m\leq 10^3$),答案对 $10^9$ 取模. 分析 ...
- 使用Costura.Fody插件将自己写的程序打包成一个可以独立运行的EXE文件
我们在开发程序的时候会引用很多DLL文件,在程序完成编写后,如果不把这些引用的DLL打包,不能在其他电脑运行,那么很多同学可能在想了,能不能把我们编写好的程序打包成一个EXE文件,最好双击就能运行,当 ...
- hive,把一个表中计算好的数据,存到另一个外部表中
直接上代码: 第一部分: case class OrdPacsresult_obj(pk_dcpv: String, result_obj: String) 第二部分: def ordsubj: Un ...
- Linux操作系统常用命令合集——第一篇-文件和目录操作(40个命令)
一.选项和参数的区别 在经过上一次的系统安装后我们已经成功登陆,登陆LInux后,我们就可以在#或$符后面去输入命令,有的时候命令后面还会跟着“选项”(英文名:options)或“参数” ...
- Cogs 12. 运输问题2(有上下界的有源汇最大流)
运输问题2 ★★☆ 输入文件:maxflowb.in 输出文件:maxflowb.out 简单对比 时间限制:1 s 内存限制:128 MB 运输问题 [问题描述] 一个工厂每天生产若干商品,需运输到 ...
- WPS-系统缺失字体
WPS-系统缺失字体 小书匠 linux 在Linux上新安装WPS后,第一次打开就出现以下问题: wps系统缺失字体 问题原因: Linux上缺少windows字体,把字体添加上去即可. 操作步骤 ...
- JDK8中好用的日期处理-LocalDate类-LocalTime-LocalDateTIme,mysql解决时区相差13小时的问题,日期格式器DateTimeFormatter
set global time_zone='+08:00'; set time_zone = '+08:00'; show variables like '%time_zone:'
- php des 对称加解密类
<?php header("Content-Type: text/html;charset=utf-8"); /** * des 对称加解密 */ class des { p ...
- python 字典元素操作
#字典创建>>> dict2 = { 'abc': 123, 98.6: 37 }>>> dict2[98.6]37>>> dict2[" ...
- 微信小程序入门与实战 常用组件API开发技巧项目实战*全
第1章 什么是微信小程序? 第2章 小程序环境搭建与开发工具介绍 第3章 从一个简单的“欢迎“页面开始小程序之旅 第4章 第二个页面:新闻阅读列表 第5章 小程序的模板化与模块化 第6章 构建新闻详情 ...