之前讲了RAC如何帮我们实现KVO / 代理 / 事件 / 通知

今天先不去分析它的核心代码, 我们先看看ReactiveObjC库里面一些特别的东西,  如果大家点开ReactiveObjC目录应该会看到很多category, 今天我们先来看看这些

我们先从UITextView+RACSignalSupport.h开始看

#import <UIKit/UIKit.h>

@class RACDelegateProxy;
@class RACSignal<__covariant ValueType>; NS_ASSUME_NONNULL_BEGIN @interface UITextView (RACSignalSupport) @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; - (RACSignal<NSString *> *)rac_textSignal; @end

这里有一个属性跟一个方法,

关于RACDelegateProxy这个类的用途大概是把初始化传入的代理绑定或者添加给当前正在处理的信号

给大家一个例子:

#import "ViewController.h"
#import <ReactiveObjC.h>
#import <objc/runtime.h> @interface ViewController ()<UITextViewDelegate> @end @implementation ViewController - (void)viewDidLoad {;
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. // 以UITextViewDelegate来初始化一个RACDelegateProxy
RACDelegateProxy *delegateProxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextViewDelegate)]; // 注册要实现的方法
[[delegateProxy
rac_signalForSelector:@selector(textViewDidBeginEditing:)]
subscribeNext:^(RACTuple * _Nullable x) { NSLog(@"开始编辑");
}]; UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(, , , )];
textView.center = self.view.center;
textView.backgroundColor = [UIColor greenColor]; // 设置代理为我们创建的RACDelegateProxy, 注意要转义不然会有警告
textView.delegate = (id<UITextViewDelegate>)delegateProxy; [self.view addSubview:textView]; // retain我们创建的delegateProxy, 避免被释放
objc_setAssociatedObject(textView, _cmd, delegateProxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end

这个一般是RAC内部使用, 我们比较少用. 另外也只能处理没有返回值的代理方法

可以到UITextview+RACSignalSupport.m里面看看, 也是类似这样用的

- (RACDelegateProxy *)rac_delegateProxy {
RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd);
if (proxy == nil) {
proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextViewDelegate)];
objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} return proxy;
}

好了, 现在我们来使用下这个UITextView类别唯一的方法

- (RACSignal<NSString *> *)rac_textSignal;

大家可以看到, 这个方法会返回一个信号 我们可以对他订阅, 试试看

UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(, , , )];
textView.center = self.view.center;
textView.backgroundColor = [UIColor greenColor]; [self.view addSubview:textView]; [[textView
rac_textSignal]
subscribeNext:^(NSString * _Nullable x) { NSLog(@"%@", x);
}];

运行看看, 当我们在textView中输入文字的时候会打印:

-- ::42.841 RAC[:]
-- ::43.353 RAC[:]
-- ::44.031 RAC[:]

所以这个x就是Textview的内容了.

下面我们看看

UITextField+RACSignalSupport.h

#import <UIKit/UIKit.h>

@class RACChannelTerminal<ValueType>;
@class RACSignal<__covariant ValueType>; NS_ASSUME_NONNULL_BEGIN @interface UITextField (RACSignalSupport) - (RACSignal<NSString *> *)rac_textSignal; - (RACChannelTerminal<NSString *> *)rac_newTextChannel; @end NS_ASSUME_NONNULL_END

这里有两个方法, 我们先看第一个rac_textSignal

    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(, , , )];

    textField.center          = self.view.center;
textField.backgroundColor = [UIColor yellowColor]; [[textField
rac_textSignal]
subscribeNext:^(NSString * _Nullable x) { NSLog(@"%@", x);
}]; [self.view addSubview:textField];

运行看看, 我们输入数字的时候会打印内容:

-- ::45.686 RAC[:]
-- ::46.139 RAC[:]
-- ::46.798 RAC[:]

然后我们看看另外一个方法

- (RACChannelTerminal<NSString *> *)rac_newTextChannel;

这里涉及到了一个类RACChannelTerminal, 我们点进去看看这个类

@interface RACChannelTerminal<ValueType> : RACSignal<ValueType> <RACSubscriber>

- (instancetype)init __attribute__((unavailable("Instantiate a RACChannel instead")));

// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value; @end

可以看到它是一个RACSignal的子类,  我们先调用看看这个方法

[[textField
rac_newTextChannel]
subscribeNext:^(NSString * _Nullable x) { NSLog(@"%@", x);
}];

效果跟rac_textSignal一样, 那么它有什么特别的用法呢

它的作用是做双向绑定 关于什么是双向绑定呢? 给大家一个简单的例子:

    UITextField *textFieldA = [[UITextField alloc] initWithFrame:CGRectMake(, , , )];

    textFieldA.center          = self.view.center;
textFieldA.backgroundColor = [UIColor yellowColor]; [self.view addSubview:textFieldA]; UITextField *textFieldB = [[UITextField alloc] initWithFrame:CGRectMake(, , , )]; textFieldB.center = CGPointMake(self.view.center.x, self.view.center.y + );
textFieldB.backgroundColor = [UIColor yellowColor]; [self.view addSubview:textFieldB]; RACChannelTerminal *terminalA = [textFieldA rac_newTextChannel];
RACChannelTerminal *terminalB = [textFieldB rac_newTextChannel]; [terminalA subscribe:terminalB];
[terminalB subscribe:terminalA];

运行可以看到, 改变textFieldA的值textFieldB的值也会跟着改变, 反过来也一样.

这里如果要实现双向绑定, 其实还有一个简单的方法:

RACChannelTo(textFieldA, text) = RACChannelTo(textFieldB, text);

大家可以试试看.

如果我们不仅仅想让两个绑定对象之间的值简单的相等而已呢? 比如textFieldA的值是123的时候textFieldB的值要为321要怎么处理呢?

这里我们先说一个一会用到的方法: map

map方法,将会创建一个和原来一模一样的信号,只不过新的信号传递的值变为了block(value)。

    [[[textField
rac_textSignal]
map:^id _Nullable(NSString * _Nullable value) { if ([value isEqualToString:@""]) { return @"";
} else { return @"";
}
}] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x);
}];

运行看看, 当我们输入1, 会打印0, 输入11的时候会打印1, 这里就是把传递的值从textField的text转变成为我们的1 和 0;

然后有个特别的地方, 加入我们知道传递的值的类型, 我们就可以直接把后面订阅的block里面的参数类型直接改成我们知道的类型

例如把id改为NSString *运行结果也是一样的, 这个是RAC一个比较特别的地方

那么要实现上面的123 到 321可以这样写:

    RACChannelTerminal *terminalA = [textFieldA rac_newTextChannel];
RACChannelTerminal *terminalB = [textFieldB rac_newTextChannel]; [[terminalA
map:^id _Nullable(id _Nullable value) { if ([value isEqualToString:@""]) { return @"";
} return value;
}]
subscribe:terminalB]; [terminalB subscribe:terminalA];

大家可以自己运行看看效果, 当textFieldA输入123的时候textFieldB会变为321

下面我们看看

UIActionSheet+RACSignalSupport.h

#import <UIKit/UIKit.h>

@class RACDelegateProxy;
@class RACSignal<__covariant ValueType>; NS_ASSUME_NONNULL_BEGIN @interface UIActionSheet (RACSignalSupport) @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; - (RACSignal<NSNumber *> *)rac_buttonClickedSignal; @end

rac_delegateProxy跟之前textview是一样的用法这里开始就不再解释这类属性了

我们直接试着使用rac_buttonClickedSignal

    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"RAC ActionSheet" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"OK" otherButtonTitles:@"Other", nil];

    [[actionSheet
rac_buttonClickedSignal]
subscribeNext:^(NSNumber * _Nullable x) { NSLog(@"%@", x);
}]; [actionSheet showInView:self.view];

运行看看, x是actionSheet上按钮的编号, 我们拿到编号就可以做响应的事件处理了.

UIAlertView+RACSignalSupport.h

@interface UIAlertView (RACSignalSupport)

@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy;

- (RACSignal<NSNumber *> *)rac_buttonClickedSignal;

- (RACSignal<NSNumber *> *)rac_willDismissSignal;

@end

NS_ASSUME_NONNULL_END

它有两个方法, 一个是点击的时候用, 一个是dismiss的时候用

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"RAC" message:@"RAC Alert" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];

    [[alert
rac_buttonClickedSignal]
subscribeNext:^(NSNumber * _Nullable x) { NSLog(@"click: x");
}]; [[alert
rac_willDismissSignal]
subscribeNext:^(NSNumber * _Nullable x) { NSLog(@"dismiss: %@", x);
}]; [alert show];

UIControl+RACSignalSupport.h

#import <UIKit/UIKit.h>

@class RACSignal<__covariant ValueType>;

NS_ASSUME_NONNULL_BEGIN

@interface UIControl (RACSignalSupport)

- (RACSignal<__kindof UIControl *> *)rac_signalForControlEvents:(UIControlEvents)controlEvents;

@end

NS_ASSUME_NONNULL_END

只有一个方法, 这个之前讲过是做UIControllerEvent处理的, 再给个例子:

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

    [button setFrame:CGRectMake(, , , )];
[button setCenter:self.view.center];
[button setBackgroundColor:[UIColor yellowColor]];
[button setTitle:@"按钮" forState:UIControlStateNormal]; [[button
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(__kindof UIControl * _Nullable x) { NSLog(@"点击了按钮");
x.backgroundColor = [UIColor redColor];
}]; [self.view addSubview:button];

UIDatePicker+RACSignalSupport.h

#import <UIKit/UIKit.h>

@class RACChannelTerminal<ValueType>;

NS_ASSUME_NONNULL_BEGIN

@interface UIDatePicker (RACSignalSupport)

- (RACChannelTerminal<NSDate *> *)rac_newDateChannelWithNilValue:(nullable NSDate *)nilValue;

@end

NS_ASSUME_NONNULL_END

它只有一个绑定的方法, 直接给大家一个例子:

大概效果为我们在Controller中添加一个UITextField跟一个UIDatePicker, 然后获取他们的RACChannelTerminal,

将UIDatePicker绑定给UITextField, 当我们滚动datePicker的时候 textField的值会跟着改变

   UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(, , self.view.frame.size.width, )];
datePicker.center = self.view.center;
datePicker.backgroundColor = [UIColor redColor]; [self.view addSubview:datePicker]; UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(, , , )];
textField.center = CGPointMake(self.view.center.x, self.view.center.y - );
textField.backgroundColor = [UIColor yellowColor]; [self.view addSubview:textField]; RACChannelTerminal *datePickerTerminal = [datePicker rac_newDateChannelWithNilValue:[NSDate date]];
RACChannelTerminal *textFieldTerminal = [textField rac_newTextChannel]; [[datePickerTerminal
map:^id _Nullable(id _Nullable value) { NSLog(@"%@", value);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
return [dateFormatter stringFromDate:value];
}]
subscribe:textFieldTerminal];

运行截图:

这篇先写到这里, 下次我们再接着看

函数响应式编程及ReactiveObjC学习笔记 (三)的更多相关文章

  1. 函数响应式编程及ReactiveObjC学习笔记 (-)

    最近无意间看到一个视频讲的ReactiveObjC, 觉得挺好用的 但听完后只是了解个大概. 在网上找了些文章, 有的写的比较易懂但看完还是没觉得自己能比较好的使用RAC, 有的甚至让我看不下去 这两 ...

  2. 函数响应式编程及ReactiveObjC学习笔记 (二)

    之前我们初步认识了RAC的设计思路跟实现方式, 现在我们再来看看如果使用它以及它能帮我们做什么 One of the major advantages of RAC is that it provid ...

  3. 函数响应式编程及ReactiveObjC学习笔记 (四)

    今天我们继续看其他的类别 UIImagePickerController+RACSignalSupport.h #import <UIKit/UIKit.h> @class RACDele ...

  4. 函数响应式编程(FRP)框架--ReactiveCocoa

    由于工作原因,有段时间没更新博客了,甚是抱歉,只是,从今天開始我又活跃起来了,哈哈,于是决定每周更新一博.大家互相学习.交流. 今天呢.讨论一下关于ReactiveCocoa,这个採用函数响应式编程( ...

  5. 函数响应式编程(FRP)—基础概念篇

    原文出处:http://ios.jobbole.com/86815/. 一函数响应式编程 说到函数响应式编程,就不得不提到函数式编程,他们俩有什么关系呢?今天我们就详细的解析一下他们的关系. 现在下面 ...

  6. RxJS入门之函数响应式编程

    一.函数式编程 1.声明式(Declarativ) 和声明式相对应的编程⽅式叫做命令式编程(ImperativeProgramming),命令式编程也是最常见的⼀种编程⽅式. //命令式编程: fun ...

  7. 函数响应式编程(FRP)思想-Callback风格

    序 ReactiveCocoa是IOS广为使用的技术框架,而ReactiveCocoa的核心思想就FRP.FRP不同于JAVA的object-oriented和AOP,FRP能让你的代码像数学一样简洁 ...

  8. ReactiveCocoa,最受欢迎的iOS函数响应式编程库(2.5版),没有之一!

    简介 项目主页: ReactiveCocoa 实例下载: https://github.com/ios122/ios122 简评: 最受欢迎,最有价值的iOS响应式编程库,没有之一!iOS MVVM模 ...

  9. 函数响应式编程(FRP)从入门到”放弃”——基础概念篇

    前言 研究ReactiveCocoa一段时间了,是时候总结一下学到的一些知识了. 一.函数响应式编程 说道函数响应式编程,就不得不提到函数式编程,它们俩到底有什么关系呢?今天我们就详细的解析一下他们的 ...

随机推荐

  1. python+requests+unittest API接口测试

    黑熊再网上查找了下接口测试相关的资料,大都重点是以数据驱动的形式,见用例维护在文本或表格中,而没有说明怎么样去生成想要的用例, 问题: 测试接口时,比如参数a,b,c,我要先测a参数,有(不传,为空, ...

  2. Awesome Hadoop

    A curated list of amazingly awesome Hadoop and Hadoop ecosystem resources. Inspired by Awesome PHP,  ...

  3. 软件测试基础(软件测试分类和工具组)firebug、firepath的安装

    白盒测试:需要了解内部结构和代码 黑盒测试:不关心内部结构和代码 灰盒测试:介于白盒黑盒之间 静态测试:测试时不执行被测试软件 动态测试:测试时执行被测试软件 单元测试:测试软件的单元模块 集成测试: ...

  4. Unity与Android交互-Unity接入高德地图实现定位以及搜索周边的功能(使用Android Studio)详细操作

    刚进公司给安排的任务就是Unity接入高德地图,算是踩了不少坑总算做出来了,抽点时间写个博客记录一下 废话不多说 先上效果图 获取定位并根据手机朝向显示周边信息            使用的Unity ...

  5. 惊心动魄的SAP S4客户额度调整运动

    今天一大早,收到商务部发来的消息,说某一个客户的额度超额了,但实际上并未超额.从SAP系统中反馈的额度来看,显示超了2万多.后来经过在BP画面检查信用段发现,这个客户额度占用里面,某一个未清订单已经部 ...

  6. python——根据电子表格的数据自动查找文件

    最近刚接触python,找点小任务来练练手,希望自己在实践中不断的锻炼自己解决问题的能力. 经理最近又布置了一个很繁琐的任务给我:有一项很重大的项目做完了,但是要过审计(反正就是类似的审批之类的事情) ...

  7. asp.net core中负载均衡场景下http重定向https的问题

    上周欣喜地发现,微软官方终于针对 asp.net core 在使用负载均衡的情况下从 http 强制重定向至 https 的问题提供了解决方法. app.UseForwardedHeaders(new ...

  8. Scrapy中使用cookie免于验证登录和模拟登录

    Scrapy中使用cookie免于验证登录和模拟登录 引言 python爬虫我认为最困难的问题一个是ip代理,另外一个就是模拟登录了,更操蛋的就是模拟登录了之后还有验证码,真的是不让人省心,不过既然有 ...

  9. Java IO学习笔记六

    打印流 在整个IO包中,打印流是输出信息最方便的类,主要包含字节打印流(PrintStream)和字符打印流(PrintWrite).打印流提供了非常方便的打印功能,可以打印任何的数据类型,例如:小数 ...

  10. Java IO学习笔记一

    Java IO学习笔记一 File File是文件和目录路径名的抽象表示形式,总的来说就是java创建删除文件目录的一个类库,但是作用不仅仅于此,详细见官方文档 构造函数 File(File pare ...