之前讲了 RAC 如何帮我们实现 KVO / 代理 / 事件 / 通知
今天先不去分析它的核心代码, 我们先看看 ReactiveObjC 库里面一些特别的东西, 如果大家点开 ReactiveObjC 目录应该会看到很多 category, 今天我们先来看看这些
我们先从 UITextView+RACSignalSupport.h 开始看
- #import
- @class RACDelegateProxy;@class RACSignal < __covariant ValueType > ;
- NS_ASSUME_NONNULL_BEGIN
- @interface UITextView(RACSignalSupport)
- @property(nonatomic, strong, readonly) RACDelegateProxy * rac_delegateProxy;
- - (RACSignal * ) rac_textSignal;
- @end
这里有一个属性跟一个方法,
关于 RACDelegateProxy 这个类的用途大概是把初始化传入的代理绑定或者添加给当前正在处理的信号
给大家一个例子:
- #import "ViewController.h"#import#import
- @interface ViewController()
- @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(0, 0, 100, 100)];
- textView.center = self.view.center;
- textView.backgroundColor = [UIColor greenColor];
- // 设置代理为我们创建的RACDelegateProxy, 注意要转义不然会有警告
- textView.delegate = (id) 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(0, 0, 100, 100)];
- textView.center = self.view.center;
- textView.backgroundColor = [UIColor greenColor];
- [self.view addSubview: textView];
- [[textView rac_textSignal] subscribeNext: ^(NSString * _Nullable x) {
- NSLog(@"%@", x);
- }];
运行看看, 当我们在 textView 中输入文字的时候会打印:
- 2017 - 07 - 23 22 : 41 : 42.841 RAC[70053 : 14036438] 1 2017 - 07 - 23 22 : 41 : 43.353 RAC[70053 : 14036438] 11 2017 - 07 - 23 22 : 41 : 44.031 RAC[70053 : 14036438] 111
所以这个 x 就是 Textview 的内容了.
下面我们看看
UITextField+RACSignalSupport.h
- #import
- @class RACChannelTerminal;@class RACSignal < __covariant ValueType > ;
- NS_ASSUME_NONNULL_BEGIN
- @interface UITextField(RACSignalSupport)
- - (RACSignal * ) rac_textSignal;
- - (RACChannelTerminal * ) rac_newTextChannel;
- @end
- NS_ASSUME_NONNULL_END
这里有两个方法, 我们先看第一个 rac_textSignal
- UITextField * textField = [[UITextField alloc] initWithFrame: CGRectMake(0, 0, 100, 35)];
- textField.center = self.view.center;
- textField.backgroundColor = [UIColor yellowColor];
- [[textField rac_textSignal] subscribeNext: ^(NSString * _Nullable x) {
- NSLog(@"%@", x);
- }];
- [self.view addSubview: textField];
运行看看, 我们输入数字的时候会打印内容:
- 2017 - 07 - 23 22 : 55 : 45.686 RAC[70205 : 14118946] 1 2017 - 07 - 23 22 : 55 : 46.139 RAC[70205 : 14118946] 11 2017 - 07 - 23 22 : 55 : 46.798 RAC[70205 : 14118946] 111
然后我们看看另外一个方法
- - (RACChannelTerminal < NSString * >*) rac_newTextChannel;
这里涉及到了一个类 RACChannelTerminal, 我们点进去看看这个类
- @interface RACChannelTerminal: RACSignal
- - (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(0, 0, 100, 35)];
- textFieldA.center = self.view.center;
- textFieldA.backgroundColor = [UIColor yellowColor];
- [self.view addSubview: textFieldA];
- UITextField * textFieldB = [[UITextField alloc] initWithFrame: CGRectMake(0, 0, 100, 35)];
- textFieldB.center = CGPointMake(self.view.center.x, self.view.center.y + 50);
- 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: @"11"]) {
- return@"1";
- } else {
- return@"0";
- }
- }] 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: @"123"]) {
- return@"321";
- }
- return value;
- }] subscribe: terminalB];
- [terminalB subscribe: terminalA];
大家可以自己运行看看效果, 当 textFieldA 输入 123 的时候 textFieldB 会变为 321
下面我们看看
UIActionSheet+RACSignalSupport.h
- #import
- @class RACDelegateProxy;@class RACSignal < __covariant ValueType > ;
- NS_ASSUME_NONNULL_BEGIN
- @interface UIActionSheet(RACSignalSupport)
- @property(nonatomic, strong, readonly) RACDelegateProxy * rac_delegateProxy;
- - (RACSignal * ) 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 * ) rac_buttonClickedSignal;
- - (RACSignal * ) 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
- @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(0, 0, 100, 35)]; [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
- @class RACChannelTerminal;
- NS_ASSUME_NONNULL_BEGIN
- @interface UIDatePicker(RACSignalSupport)
- - (RACChannelTerminal * ) rac_newDateChannelWithNilValue: (nullable NSDate * ) nilValue;
- @end
- NS_ASSUME_NONNULL_END
它只有一个绑定的方法, 直接给大家一个例子:
大概效果为我们在 Controller 中添加一个 UITextField 跟一个 UIDatePicker, 然后获取他们的 RACChannelTerminal,
将 UIDatePicker 绑定给 UITextField, 当我们滚动 datePicker 的时候 textField 的值会跟着改变
- UIDatePicker * datePicker = [[UIDatePicker alloc] initWithFrame: CGRectMake(0, 0, self.view.frame.size.width, 160)];
- datePicker.center = self.view.center;
- datePicker.backgroundColor = [UIColor redColor];
- [self.view addSubview: datePicker];
- UITextField * textField = [[UITextField alloc] initWithFrame: CGRectMake(0, 0, 180, 35)];
- textField.center = CGPointMake(self.view.center.x, self.view.center.y - 100);
- 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];
运行截图:
这篇先写到这里, 下次我们再接着看
来源: http://www.cnblogs.com/zhouxihi/p/7226917.html