ReactiveCocoa用法示例(二)
来源:互联网 发布:中世纪2王国优化9补丁 编辑:程序博客网 时间:2024/05/16 04:12
知识点
- RACSignal与OC对象方法的绑定
- RACSignal与OC对象属性的绑定
- [RACSignal merge:], Merge操作的使用
- [RACSignal combineLatest: reduce:^id(){}], Combine和reduce操作的使用
- [RACSignal return:nil] 与[RACSignal empty], nil信号和空信号的区别
示例项目功能
- 点击电子发票 纸质发票 个人 公司四个按钮 界面作出变化
- 根据四种状态检查Textfiled内容是否输入错误
- 输入错误Toast弹出错误内容
- 输入正确Alert弹出确认信息
示例项目地址
github: https://github.com/heeween/RACComand2.git
用到的城市选择器属于Jonhory
构建电子发票 纸质发票 个人 公司四个按钮的RACCommand对象
// GBInvoiceContentViewModel.m中代码 self.personalCmd = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { return [RACSignal return:@(Invoice_Owner_Personal)]; }]; self.companyCmd = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { return [RACSignal return:@(Invoice_Owner_Company)]; }]; self.nonPaperCmd = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { return [RACSignal return:@(Invoice_Source_NonPaper)]; }]; self.paperCmd = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { return [RACSignal return:@(Invoice_Source_Paper)]; }];
在控制器中将RACCommand对象和按钮事件绑定
// GBInvoiceContentController.m 中代码 self.contentView.ownerItemView.personalButton.rac_command = self.viewModel.personalCmd; self.contentView.ownerItemView.companyButton.rac_command = self.viewModel.companyCmd; self.sourceView.nonPaperButton.rac_command = self.viewModel.nonPaperCmd; self.sourceView.paperButton.rac_command = self.viewModel.paperCmd;
将上述四个command的包含的信号合并为source何owner两个信号
// GBInvoiceContentViewModel.m中代码 RACSignal *personalSignal = [[self.personalCmd executionSignals] switchToLatest]; RACSignal *companySignal = [[self.companyCmd executionSignals] switchToLatest]; RACSignal *nonPaperSignal = [[self.nonPaperCmd executionSignals] switchToLatest]; RACSignal *paperSignal = [[self.paperCmd executionSignals] switchToLatest]; self.ownerSignal = [RACSignal merge:@[personalSignal,companySignal]]; self.sourceSignal = [RACSignal merge:@[nonPaperSignal,paperSignal]];
分别将source和owner信号绑定对应的view上
// GBInvoiceContentController.m 中代码 RAC(self.sourceView,type) = self.viewModel.sourceSignal; RAC(self.confirmView,type) = self.viewModel.sourceSignal; RAC(self.addressView,type) = self.viewModel.sourceSignal; RAC(self.contentView,type) = self.viewModel.ownerSignal;
将source和owner两个信号合并为动画信号
// GBInvoiceContentViewModel.m中代码 self.animateSignal = [RACSignal merge:@[self.ownerSignal,self.sourceSignal]];
将动画信号绑定到控制器的动画方法
// GBInvoiceContentController.m 中代码 [self rac_liftSelector:@selector(animatieUpdateSubview:) withSignals:self.viewModel.animateSignal, nil];/** 动画子控件 */- (void)animatieUpdateSubview:(id)obj { [UIView animateWithDuration:0.3 animations:^{ [self.view layoutIfNeeded]; }];}
收集控制器中各输入框的信号,并且绑定到viewmode中发票对象的属性上
// GBInvoiceContentController.m 中代码 self.viewModel.companyNameSignal = self.contentView.companyNameItemView.inputField.rac_textSignal; self.viewModel.taxNumberSignal = self.contentView.taxPayerIDItemView.inputField.rac_textSignal; self.viewModel.remarkSignal = self.contentView.markField.rac_textSignal; self.viewModel.receiverSignal = self.addressView.contactNameItemView.inputField.rac_textSignal; self.viewModel.phoneSignal = self.addressView.contactPhoneItemView.inputField.rac_textSignal; self.viewModel.regionSignal = self.addressView.contactAddressItemView.inputField.rac_textSignal; self.viewModel.minAddressSignal = self.addressView.contactAddressDescItemView.inputField.rac_textSignal; self.viewModel.emailSignal = self.addressView.contactMailItemView.inputField.rac_textSignal; RAC(self.viewModel.invoice,companyName) = self.viewModel.companyNameSignal; RAC(self.viewModel.invoice,taxNumber) = self.viewModel.taxNumberSignal; RAC(self.viewModel.invoice,receiver) = self.viewModel.receiverSignal; RAC(self.viewModel.invoice,region) = self.viewModel.regionSignal; RAC(self.viewModel.invoice,minAddress) = self.viewModel.minAddressSignal; RAC(self.viewModel.invoice,email) = self.viewModel.emailSignal;
在viewmodel中根据source和owner信号,对各个输入框的信号进行merge和reduce操作,整合成错误字符串信号errorStringSignal
// GBInvoiceContentViewModel.m中代码- (RACSignal *)errorStringSignal { if (!_errorStringSignal) { RACSignal *companyNameError = [self.companyNameSignal map:^id(NSString *value) { if (value.length <= 0) { return @"公司名称不能为空"; }else { return @""; } }]; RACSignal *taxNumberError = [self.taxNumberSignal map:^id(NSString *value) { if (value.length <= 0) { return @"纳税人识别号不能为空"; }else if (![value isTaxNumber]) { return @"纳税人识别号输入不正确"; }else { return @""; } }]; RACSignal *receiverError = [self.receiverSignal map:^id(NSString *value) { return value.length > 0 ? @"" : @"联系人不能为空"; }]; RACSignal *phoneError = [self.phoneSignal map:^id(NSString *value) { if (value.length <= 0) { return @"电话不能为空"; }else if (value.length != 11) { return @"电话输入不正确"; }else { return @""; } }]; RACSignal *regionError = [self.regionSignal map:^id(NSString *value) { return value.length > 0 ? @"" : @"地址不能为空"; }]; RACSignal *minaddressError = [self.minAddressSignal map:^id(NSString *value) { return value.length > 0 ? @"" : @"详细地址不能为空"; }]; RACSignal *emailError = [self.emailSignal map:^id(NSString *value) { if (value.length <= 0) { return @"邮箱不能为空"; }else if (![value isEmail]) { return @"邮箱输入不正确"; }else { return @""; } }]; NSDictionary *sourceDict = @{ @(Invoice_Source_Paper): [RACSignal combineLatest:@[receiverError,phoneError,regionError,minaddressError] reduce:^id(NSString *receiver, NSString *phone,NSString *region,NSString *minaddress){ NSMutableArray *array = [NSMutableArray array]; if (receiver.length > 0) { [array addObject:receiver]; } if (phone.length > 0) { [array addObject:phone]; } if (region.length > 0) { [array addObject:region]; } if (minaddress.length > 0) { [array addObject:minaddress]; } return array; }], @(Invoice_Source_NonPaper): [RACSignal combineLatest:@[receiverError,phoneError,emailError] reduce:^id(NSString *receiver, NSString *phone,NSString *email){ NSMutableArray *array = [NSMutableArray array]; if (receiver.length > 0) { [array addObject:receiver]; } if (phone.length > 0) { [array addObject:phone]; } if (email.length > 0) { [array addObject:email]; } return array; }] }; RACSignal *sourceError = [RACSignal switch:self.sourceSignal cases:sourceDict default:nil]; NSDictionary *ownerDict = @{ @(Invoice_Owner_Personal): [RACSignal return:nil], @(Invoice_Owner_Company): [RACSignal combineLatest:@[companyNameError,taxNumberError] reduce:^id(NSString *companyName, NSString *taxNumber){ NSMutableArray *array = [NSMutableArray array]; if (companyName.length > 0) { [array addObject:companyName]; } if (taxNumber.length > 0) { [array addObject:taxNumber]; } return array; }] }; RACSignal *ownerError = [RACSignal switch:self.ownerSignal cases:ownerDict default:nil]; _errorStringSignal = [RACSignal combineLatest:@[sourceError,ownerError] reduce:^id(NSArray *sourceArray, NSArray *ownerArray){ NSMutableArray *array = [NSMutableArray array]; [array addObjectsFromArray:sourceArray]; [array addObjectsFromArray:ownerArray]; return array; }]; } return _errorStringSignal;}
把errorStringSignal和errorstring属性
- 这步看似多次一举,实则是因为页面需求并不是每次有错误就弹出,而是在点击下一步的时候才弹出
- 因为先用errorstring属性保存错误值,点击下一步的通过subscriper发出
// GBInvoiceContentController.m 中代码 RAC(self.viewModel,errorStrings) = self.viewModel.errorStringSignal;
创建下一步command,有错误senderror,没有sendcomplete
// GBInvoiceContentViewModel.m中代码- (RACCommand *)nextCmd { if (!_nextCmd) { @weakify(self); _nextCmd = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { @strongify(self); if (self.errorStrings == nil || self.errorStrings.count <= 0) { [subscriber sendNext:[self.invoice showParame]]; [subscriber sendCompleted]; }else { [subscriber sendError:[NSError errorWithDomain:@"输入有误" code:0 userInfo:@{@"errorStrings":self.errorStrings}]]; } return nil; }]; }]; } return _nextCmd;}
下一步command与下一步按钮绑定,并且绑定成功和失败信号到对应的控制器方法
// GBInvoiceContentController.m 中代码 self.confirmView.confirmButton.rac_command = self.viewModel.nextCmd; [self rac_liftSelector:@selector(showAlert:) withSignals:[[self.viewModel.nextCmd executionSignals] switchToLatest], nil]; [self rac_liftSelector:@selector(showError:) withSignals:self.viewModel.nextCmd.errors, nil];/** 输入错误提醒 */- (void)showError:(NSError *)error { NSArray *errorStrings = error.userInfo[@"errorStrings"]; [CSToastManager setDefaultPosition:CSToastPositionCenter]; [self.view makeToast:[errorStrings componentsJoinedByString:@" | "]];}/** 输入成功弹窗 */- (void)showAlert:(id)obj { GBInvoiceAlert *alertView = [GBInvoiceAlert showWith:obj confirmBlock:nil]; alertView.confirmBtn.rac_command = self.viewModel.confirmCmd;}
创建comfirmCmd
// GBInvoiceContentViewModel.m中代码- (RACCommand *)confirmCmd { if (!_confirmCmd) { _confirmCmd = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { if (arc4random() % 2 == 0) { [subscriber sendNext:@"开票成功"]; [subscriber sendCompleted]; }else { NSError *error = [NSError errorWithDomain:@"开票失败" code:00 userInfo:nil]; [subscriber sendError:error]; } return nil; }]; }]; } return _confirmCmd;}
最后一步绑定弹窗确定按钮和comfirmCmd,并且绑定成功和失败信号到对应的控制器方法
// GBInvoiceContentController.m 中代码 // 绑定弹窗的成功和失败信号 { [self rac_liftSelector:@selector(postParamSuccess:) withSignals:[[self.viewModel.confirmCmd executionSignals] switchToLatest], nil]; [self rac_liftSelector:@selector(postParamFailure:) withSignals:self.viewModel.confirmCmd.errors, nil]; }/** 开票成功 */- (void)postParamSuccess:(id)obj { [self.view makeToast:@"开票成功"];}/** 开票失败 */- (void)postParamFailure:(id)obj { [self.view makeToast:@"开票失败"];}
1,使用RACComand和RACSigna,可以很方便的拿到各自独立的事件或信号.
2, RAC有大量的信号操作,可以非常方便做业务逻辑.不管是信号合并信号转化都不需要蛋疼的中间变量.
3,RAC有吊炸天的绑定,更是方便了从结果到界面的one step.
阅读全文
0 0
- ReactiveCocoa用法示例(二)
- ReactiveCocoa用法示例(一)
- ReactiveCocoa之常见用法(二)
- ReactiveCocoa之常见用法(二)
- ReactiveCocoa<二>
- ReactiveCocoa 基本用法
- ReactiveCocoa 基本用法
- ReactiveCocoa的简单用法
- ReactiveCocoa常见用法练习
- ReactiveCocoa学习(二)
- ReactiveCocoa学习(二)
- reactiveCocoa实践二
- ReactiveCocoa框架学习(二)
- ReactiveCocoa入门教程二
- ReactiveCocoa入门教程二
- ReactiveCocoa讲解之---二
- ReactiveCocoa之RACSubject用法(四)
- ReactiveCocoa之RACSubject用法(四)
- DevOps 是一种技术不如说是一种文化
- turtlebot2启动不了:Battery : unable to check laptop battery state 的解决方案
- ReactiveCocoa用法示例(一)
- android sdk | API level 24 | R.animator
- 简单应用shiro
- ReactiveCocoa用法示例(二)
- C# 中distinct的使用与两个类的比较
- B
- Linux教程(三)安装mysql
- vim 安装自动补全python代码功能
- mybatis之抛弃XML,拥抱注解
- Android Studio3.0更新之路(遇坑必入)
- JMeter 参数化、检查点、集合点
- Uboot源码分析移植