提示:
- 1.请着重看代码块的内容,希望大家也能跟着我的代码敲一遍!大概了解一下 ReactiveCocoa 的代码风格~
- 2.我要提示的是需要用 cocoapods 安装 RAC,这期间有很多坑,不建议用 Xcode 8版本,建议用 Xcode7版本跟敲代码!因为 RAC 新版本改版很多,我还没有来得及更新
主Bundle 栏
![](https://img.haomeiwen.com/i2932245/48dad0a43565ed15.png)
文本框上的变化
动态的反映到调试去内:
![](https://img.haomeiwen.com/i2932245/dc273703b44b1bb6.gif)
Person.h 文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
/**
* 名字:
*/
@property (nonatomic, copy) NSString *name ;
/**
* 年龄:
*/
@property (nonatomic, assign) NSInteger age ;
@end
** 模型 ---> UI 的绑定:把模型上的数据放到 UI 上显示:
关键语句:
RAC(TARGET, ...) = RACObserve(TARGET, KEYPATH) ; **
**UI --->模型的绑定:
核心语句:
[[RACSignal combineLatest:
@[nameTextField.rac_textSignal,
ageTextField.rac_textSignal]]
subscribeNext:
^(RACTuple *x) {
//first:name文本框里的内容 ;
_person.name = x.first ;
//second:age 文本框里的内容 ; }] ;
_person.age = [[x second] integerValue] ;
**
ViewController.m 文件
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import "Person.h"
/**
* int转 NSString 时候会用到这个宏定义!!!
*/
#define NSStringFromValue(value) [value description]
@interface ViewController ()
@property (nonatomic, weak) UIButton *button ;
@end
@implementation ViewController {
Person *_person ;
}
/**
* MVVM设计模式:
使用 ViewModel 的原因:
1.可以把独立的业务逻辑代码(如:网络请求等)从控制器中抽取,便于测试 ;
2.大型的架构级项目中 "MVC" 设计模式对控制器的依赖太严重!用 MVVM 可以简化 ViewController 的代码
RAC 配合 MVVM:
双向绑定
3.1ReactiveCocoa 配合 MVVM 设计模式可以进行双向绑定(Model 的改变告诉 View,View 的改变也能告诉 Model)-->RAC 几乎是目前唯一的选择!
*/
- (void)viewDidLoad {
[super viewDidLoad];
//准备数据:
_person = [[Person alloc] init] ;
_person.name = @"zhangsan" ;
_person.age = 18 ;
[self dataBindEachOther] ;
// [self buttonDemo] ;
// [self textFieldCombineDemo1] ;
// [self textFieldCombineDemo2] ;
}
#pragma mark - textFieldCombineDemo1 - 组合文本框1
- (void)textFieldCombineDemo1 {
//passwordTextField
UITextField *passwordTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 80, 300, 40)] ;
passwordTextField.borderStyle = UITextBorderStyleRoundedRect ;
[self.view addSubview:passwordTextField] ;
//nameTextField
UITextField *nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 40, 300, 40)] ;
nameTextField.borderStyle = UITextBorderStyleRoundedRect ;
[self.view addSubview:nameTextField] ;
//监听文本输入目录:
//订阅 subscribe 那个信号就可以监听那个信号!
//passwordTextFieldSignal:
RACSignal *passwordTextFieldSignal = [passwordTextField rac_textSignal] ;
[passwordTextFieldSignal subscribeNext:^(id x) {
NSLog(@"%@----%@" , x , [x class]) ;
}] ;
//nameTextFieldSignal:
RACSignal *nameTextFieldSignal = [nameTextField rac_textSignal] ;
[nameTextFieldSignal subscribeNext:^(id x) {
NSLog(@"%@----%@" , x , [x class]) ;
}] ;
/**
* 现在有两个信号:
1.passwordTextFieldSignal
2.nameTextFieldSignal
* 在 RAC 中还有一个组合信号的概念~感觉这些和之前Apple 原生的一些代码比如多线程里的 "组队列" 的概念有些相似!我只要订阅这个组合信号就可以监听到这个组合信号里的所有的信号的变化,不用订阅组里的每一个信号一一订阅 ;
*/
//combineLatest:组合信号:
RACSignal *combineLatestSignal = [RACSignal combineLatest:@[passwordTextFieldSignal , nameTextFieldSignal]] ;
[combineLatestSignal subscribeNext:^(RACTuple *x) {
NSLog(@"combineLatest---%@----%@" , x , [x class]) ;
NSString *name = x.first ;
NSString *password = x.second ;
NSLog(@"%@*******%@" , name , password) ;
}] ;
//RACTuple:RAC元组:(类似字典,可以包多个值) ;
}
#pragma mark - textFieldCombineDemo2 - 组合文本框2
/**
* 系统提供的信号是始终存在的才能保证随时发生事件才能扑捉得到,因此,所有的 block 中,如果调用"self."或者"成员变量"点语法几乎肯定会出现循环引用!!!
*/
- (void)textFieldCombineDemo2 {
//nameTextField
UITextField *nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 40, 300, 40)] ;
nameTextField.placeholder = @"nameTextField" ;
nameTextField.borderStyle = UITextBorderStyleRoundedRect ;
[self.view addSubview:nameTextField] ;
RACSignal *nameTextFieldSignal = [nameTextField rac_textSignal] ;
//passwordTextField
UITextField *passwordTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 80, 300, 40)] ;
passwordTextField.placeholder = @"passwordTextField" ;
passwordTextField.borderStyle = UITextBorderStyleRoundedRect ;
[self.view addSubview:passwordTextField] ;
RACSignal *passwordTextFieldSignal = [passwordTextField rac_textSignal] ;
/**
* 现在有两个信号:
1.passwordTextFieldSignal
2.nameTextFieldSignal
* 在 RAC 中还有一个组合信号的概念~感觉这些和之前Apple 原生的一些代码比如多线程里的 "组队列" 的概念有些相似!我只要订阅这个组合信号就可以监听到这个组合信号里的所有的信号的变化,不用订阅组里的每一个信号一一订阅 ;
*/
//combineLatest:组合信号:
//reduce:^id:返回值为 id 类型 ;
//reduce: 减少 -->合并两个信号的数据进行汇总计算的时候使用:
//在 reduce 中通过接受参数进行计算,并且返回需要的数值:\
//for instance(例如): 只有用户名和密码同事存才的时候才允许登录(下一步操作)!
RACSignal *combineLatestSignal = [RACSignal combineLatest:@[nameTextFieldSignal , passwordTextFieldSignal] reduce:^id (NSString *name , NSString *password) {
NSLog(@"name = %@######password = %@" , name , password) ;
//判断两者是否同时存在:
return @(name.length > 0 && password.length > 0) ;
}] ;
//解除循环引用的方法1:
// __weak typeof(self) weakSelf = self ;
//⭐️解除循环引用的方法2-->RAC 特有的方法:
//@weakify(self) 的后面可以不加分号!!!
@weakify(self)
//信号被订阅之后才是热信号!没有被订阅的信号称为"冷信号":
[combineLatestSignal subscribeNext:^(id x) {
//x = 0: 说明用户名和密码不同时存在, x = 1:用户名与密码同时存才:
NSLog(@"combineLatest---%@----%@" , x , [x class]) ;
//根据 x 的布尔值( boolValue )显示 button 与否:
//weakSelf.button.enabled = [x boolValue] ;
//⭐️解除循环引用的方法2-->RAC 特有的方法:
//这里用@strongify(self)
//相当于 weak - strong dance :强弱舞蹈:
@strongify(self)
self.button.enabled = [x boolValue] ;
}] ;
}
#pragma mark - buttonDemo
- (void)buttonDemo {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd] ;
btn.center = self.view.center ;
[self.view addSubview:btn] ;
//监听事件:不在需要新建一个方法!这种写法在现在的 iOS 还是比较常见的!
//用的是在 block 里面写方法! bloc 可以把所有的相关代码都放在一起!
RACSignal *signal = [btn rac_signalForControlEvents:UIControlEventTouchUpInside] ;
[signal subscribeNext:^(id x) {
NSLog(@"btn = %@" , x) ;
}] ;
_button = btn ;
}
#pragma mark - dealloc
//检测是否调用了 dealloc 方法:
- (void)dealloc {
NSLog(@"%s" , __FUNCTION__) ;
}
#pragma mark - dataBindEachOther - 双向绑定
- (void)dataBindEachOther {
UITextField *nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 60, 350, 50)] ;
nameTextField.borderStyle = UITextBorderStyleRoundedRect ;
[self.view addSubview:nameTextField] ;
UITextField *ageTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 150, 350, 50)] ;
ageTextField.borderStyle = UITextBorderStyleRoundedRect ;
[self.view addSubview:ageTextField] ;
/**
* 双向绑定:--->
**/
/**
* 模型 ---> UI 的绑定:把模型上的数据放到 UI 上显示:
关键语句:RAC(TARGET, ...) = RACObserve(TARGET, KEYPATH) ;
*
*/
RAC(nameTextField , text) = RACObserve(_person, name) ;
/**
* RAC(ageTextField , text) = [RACObserve(_person, age) ;赋值类型不匹配! age 赋值给 text:
* 崩溃提示:
reason: '-[__NSCFNumber rangeOfCharacterFromSet:]: unrecognized selector sent to instance 0xb000000000000123'
* RAC 出现问题后调用堆栈 "深不见底" -->调试起来难度极大!
*/
/**
* 如果使用基本数据类型绑定 UI 的内容,需要使用 map 函数,通过 block 对 value 的数值进行转换之后才能够进行绑定!
*
*/
RAC(ageTextField , text) = [RACObserve(_person, age) map:^id(id value) {
//遇到不知道的这种 id 返回值时,想都不要想~就是 NSLog 打印一下看看:
NSLog(@"%@^^^^^^^%@" , value , [value class]) ;
/**
* 开发中,经常会用到将int格式化成NSString,还在使用format(@"%d",int);那就过时了。
#define NSStringFromValue(value) [value description]
*/
return NSStringFromValue(value) ;
}] ;
/**
* UI ---> 模型 的绑定:
核心语句:
[[RACSignal combineLatest:@[nameTextField.rac_textSignal , ageTextField.rac_textSignal]] subscribeNext:^(RACTuple *x) {
_person.name = x.first ;//first:name文本框里的内容 ;
_person.age = [[x second] integerValue] ;//second:age 文本框里的内容 ;
}] ;
*/
[[RACSignal combineLatest:@[nameTextField.rac_textSignal , ageTextField.rac_textSignal]] subscribeNext:^(RACTuple *x) {
_person.name = x.first ;//first:name文本框里的内容 ;
_person.age = [[x second] integerValue] ;//second:age 文本框里的内容 ;
}] ;
//添加按钮输出结果:
UIButton *button = [UIButton buttonWithType:UIButtonTypeContactAdd] ;
button.center = self.view.center ;
[self.view addSubview:button] ;
//RAC响应事件:
[[button rac_signalForControlEvents: UIControlEventTouchUpInside] subscribeNext:^(id x) {
//这里调用了 _person 这个成员变量,会造成循环引用:
NSLog(@"%@-----%zd" , _person.name, _person.age) ;
}] ;
}
@end
网友评论