项目中学习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 ...
随机推荐
- Vue项目中v-bind动态绑定src路径不成功
问题: 在做Vue项目的时候,由于项目需求,需要动态绑定img的src时,突然发现如果说是直接请求后台接口的图片地址就能显示, 但是直接动态绑定img的src的图片的相对路径或者是绝对路径的时候,图片 ...
- 49、[源码]-Spring容器创建-创建Bean准备
49.[源码]-Spring容器创建-创建Bean准备
- [RxJS] Convert a Node.js style callback to Observable: bindNodeCallback
It's just like bindCallback, but the callback is expected to be of type callback(error, result). imp ...
- text-transform
text-transform 语法: text-transform:none | capitalize | uppercase | lowercase | 默认值:none 适用于:所有元素 继承性 ...
- git merge 及 git rebase的区别
Git上合并代码有git merge 及 git rebase 两种方式. 前置知识点 Master分支:首先,代码库应该有一个.且仅有一个主分支.所有提供给用户使用的正式版本,都在这个主分支上发布. ...
- sql server 计算属性,计算字段的用法与解析
SQL学习之计算字段的用法与解析 一.计算字段 1.存储在数据库表中的数据一般不是应用程序所需要的格式.大多数情况下,数据表中的数据都需要进行二次处理.下面举几个例子. (1).我们需要一个字段同 ...
- 使用webuploader实现大文件断点续传
IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交下载请求时,服务 ...
- [Luogu] 选学霸
https://www.luogu.org/problemnew/show/P2170 并查集+DP #include <iostream> #include <cstring> ...
- 洛谷P1016 旅行家的预算 题解
主要就是注意一下各个变量的类型别弄混了 https://www.luogu.org/problem/P1016 #include<cstdio> using namespace std; ...
- easy-table-vue+VueJs、SpringBoot+Mybatis实现MVVM模型前后台数据交互
该项目分为前端展示部分和后台服务部分. 前端部分 使用的技术是:NodeJs.Webpack.VueJs 使用的组件库是:IVIEW.easy-table-vue 使用的开发工具是:WebStorm ...