-
UIControl
- 监听 control 点击
- 从此告别
addTarget
和btnClick
[[self.loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *btn) { // btn, 即 self.loginBtn // 这里执行点击之后的操作}];复制代码
-
UITextField
- 监听 textField 的 text 改变
- 告别
UITextFieldDelegate
[self.myTF.rac_textSignal subscribeNext:^(NSString* text) { // text 即 self.myTF.text // 每当 text 有改变时,就会进入到这个 block 中}];复制代码
-
UILabel
- 把 label 的属性
text
绑定在UITextField
上
// self.myLab.text 随着 self.myTF.text 的改变而改变RAC(self.myLab, text) = self.myTF.rac_textSignal; 复制代码
- 把 label 的属性
-
监听属性改变
- 一旦 person 的 name 有变化,就进入 block 里面
- 再不用写繁琐的 KVO
[RACObserve(self.per, name) subscribeNext:^(NSString *name){ // name 即 self.per.name}];复制代码
-
监听通知信息
-
一旦键盘 frame 有所变化,就进入 block 里面
-
内部已经进行通知释放,不需要再
dealloc
中 移除
[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillChangeFrameNotification object:nil] // 这句不可少,表示 当 self 将要 dealloc 的时候,就要释放 通知 takeUntil:self.rac_willDeallocSignal] subscribeNext:^(NSNotification *notification) { NSLog(@"-----%@", notification.description);}];复制代码
- 发送通知还是之前的办法
[[NSNotificationCenter defaultCenter] postNotificationName:@"通知名称" object:nil];复制代码
-
-
数组
- 遍历
NSArray *array = @[@1, @2, @3, @4, @5];[array.rac_sequence.signal subscribeNext:^(id x) { // x 即 数组 array 的元素 }];复制代码
- 过滤
filter
,并获取过滤后的数组
NSArray *filter = [[array.rac_sequence filter:^BOOL(id value) { return [value integerValue] > 2;}] array];复制代码
- 匹配、映射
map
,变换元素并获取新数组
NSArray *map = [[array.rac_sequence map:^id(id value) { NSInteger a = [value integerValue] * [value integerValue]; return [NSString stringWithFormat:@"%ld", a]; }] array];复制代码
-
字典
rac_keySequence
和rac_valueSequence
跟数组一样rac_sequence
需要RACTupleUnpack
解包
NSDictionary *dic = @{@"name": @"lion", @"age": @18};[dic.rac_sequence.signal subscribeNext:^(id x) { RACTupleUnpack(NSString *key, NSString *value) = x; NSLog(@"\r\nkey: %@\r\nvalue: %@", key, value);}];复制代码
-
最经典的登录界面,登录按钮是否可点击
- 先联合两个信号
- 再解析信号结果
- 最后把结果绑定到信号上
RAC(self.loginBtn, enabled) = [RACSignal combineLatest:@[self.usernameTF.rac_textSignal, self.passwordTF.rac_textSignal] reduce:^id(NSString *username, NSString *password){ return @(username.length > 6 && password.length > 8); }];复制代码
-
节流
throttle
- 表示 指定时间间隔内,不再发送信号
- 这里添加
throttle
, 表示在 0.5 秒内 text 没有改变时,才会进行搜索请求 - 比如想搜索
reactiveCocoa
,如果不添加throttle
,那么每输入一个字符,都会进行搜索请求,明显不是我们想要的。
[[[self.searchTF rac_textSignal] throttle:0.5] subscribeNext:^(id x) { NSLog(@"开始搜索请求==%@", x);}];复制代码
-
一通组合拳:
-
判断用户名是否符合规则,
-
用户名正确之后,进行判断密码是否符合规则
-
密码正确,进行请求验证,成功之后返回请求结果
-
返回主线程根据结果更新界面 UI
// 1. 判断用户名- (RACSignal *)validUsernameSignal { // 防止循环引用 @weakify(self); // 因为返回的是一个信号,所以需要创建 return [RACSignal createSignal:^RACDisposable *(idsubscriber) { @strongify(self); [[[[[self.usernameTF rac_textSignal] // skip, 忽略,表示忽略几次信号 // 这里两次分别是,首次加载的时候,和 TF 第一次响应的时候, // 可以把后面的 distinctUntilChanged 注释,测试下效果 skip:2] // map, 变换,把信号内容的类型变换为另一种类型 // 把字符串根据长度转换为 bool 类型的对象 map:^id(NSString *value) { return @(value.length > 5); // distinctUntilChanged 只有值不同时,才会发送信号 // 这里为了防止,在值不满足/已满足 要求时,还继续发送信号 }] distinctUntilChanged] // 这里订阅的信息,就是转换后的 bool 类型的对象,非 1 即 0 subscribeNext:^(id x) { if ([x integerValue] == 1) { [subscriber sendNext:@"用户名正确"]; [subscriber sendCompleted]; NSLog(@"-------用户名正确"); } else { NSLog(@"-------用户名错误"); } }]; return nil; }];}// 3. 请求验证- (RACSignal *)loginSuccessSignalWithPassword: (NSString *)psw { return [RACSignal createSignal:^RACDisposable *(id subscriber) { /** * 这里可以进行请求验证用户名密码,并返回结果 */ NSString *str = @"用户信息 Data"; NSLog(@"----登陆成功并返回用户信息 Data"); NSDictionary *dic = @{@"data": str}; [subscriber sendNext:dic]; [subscriber sendCompleted]; return nil; }];}// 2、4、密码验证和界面更新- (void)loginResult { // 用户名的判断 [[[[[[[self validUsernameSignal] // then, 只有前面的信号 发送信息,并完成才会继续 // 下面进行 密码的判断 then:^RACSignal *{ return self.passwordTF.rac_textSignal; }] // throttle,间隔 0.5 秒, // 防止在输入密码过程中不断发送信号,优化性能 throttle:0.5] // filter,过滤 ,只有符合要求的才能继续 filter:^BOOL(NSString *value) { return [value length] > 6; }] // flattenMap 根据源信号的内容生成新的信号,后续订阅、监听的就是新信号的内容 // 常用于在信号嵌套中,处理信号中的信号 // 这里使用 flattenMap 生成新的信号,并且在信号内发送登录结果信息,以便后续传递信息 flattenMap:^RACStream *(NSString *value) { return [self loginSuccessSignalWithPassword:value]; }] // deliverOn 切换线程, // RACScheduler, 即 RAC 中的多线程 // 切换为主线程 deliverOn:[RACScheduler mainThreadScheduler]] // 订阅信号,获取信号传递的数据 data subscribeNext:^(id x) { NSLog(@"获取信息 \r\n%@,\r\n更新 UI", x); }];}复制代码
小结:
-
ReactiveCocoa
虽然说是思想上的巨大改变,但我更倾向于把它当做一种新型语法、更简便的语法来使用,在使用过程中,自然而然地就会体会它跟你以往编程的不同。使用多了,就会发现,原来这就是函数式响应式编程。 -
语法上的使用,只是理解编程思想的入口,思想上的一小步才是编程上的一大步。
-
理论是实践出来的,在你不懂理论的时候,那就赶紧实践吧。