简介
什么是RAC?ReactiveCocoa(简称RAC),是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。借用RayWenderlich上面的话:
As an iOS developer, nearly every line of code you write is in reaction to some event; a button tap, a received network message, a property change (via Key Value Observing) or a change in user’s location via CoreLocation are all good examples. However, these events are all encoded in different ways; as actions, delegates, KVO, callbacks and others. ReactiveCocoa defines a standard interface for events, so they can be more easily chained, filtered and composed using a basic set of tools.
翻译过来就是:
作为一个iOS开发者,你写的每一行代码几乎都是在响应某个事件,例如按钮的点击,收到网络消息,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation)
。但是这些事件都用不同的方式来处理,比如action、delegate、KVO、callback等
。ReactiveCocoa为事件定义了一个标准接口,从而可以使用一些基本工具来更容易的连接、过滤和组合。
RAC是由Matt Thompson大神开发的,很多开发者对其的评价是开启一个新的Objective-C纪元。
- 以下是RAC的Github主页:ReactiveCocoa
- 以及官方给出的用法链接
安装
ReactiveCocoa用pod安装即可。
本文的Demo可在文章最后下载,在阅读本文的时候,强烈推荐边看Demo边看博文。
项目中加入了ReactiveCocoa和DeveloperLx的打印插件LxDBAnything同时加入了键盘相应的第三方IQKeyboardManager,以及Masonry,使用方法可以看这篇文章:iOS – Masonry自动布局(Autolayout)。
代码
第一部分 简单使用
文本框事件
原来我们在使用textFiled的时候我们需要写到
[textField addTarget:self action:@selector(textChanged:) forControlEvents:UIControlEventEditingChanged];
然后实现textChanged:方法,在RAC中,对于文本框的监听,是非常简单的一件事情,看如下代码:
UITextField * textField = ({
UITextField * textField = [[UITextField alloc]init];
textField.backgroundColor = [UIColor cyanColor];
textField;
});
[self.view addSubview:textField];
@weakify(self); // __weak __typeof__(self) self_weak_ = self;
[textField mas_makeConstraints:^(MASConstraintMaker *make) {
@strongify(self); // __strong __typeof__(self) self = self_weak_;
make.size.mas_equalTo(CGSizeMake(180, 40));
make.center.equalTo(self.view);
}];
[[textField rac_signalForControlEvents:UIControlEventEditingChanged]
subscribeNext:^(id x) {
LxDBAnyVar(x);
}];
[textField.rac_textSignal subscribeNext:^(NSString *x) {
LxDBAnyVar(x);
}];
打印结果:
📍__31-[ViewController textFiledTest]_block_invoke_2 + 215🎈 x = 12
📍__31-[ViewController textFiledTest]_block_invoke241 + 211🎈 x = 0x7fe810c51a90; frame = (97.5 313.5; 180 40); text = '123'; clipsToBounds = YES; opaque = NO; gestureRecognizers = 0x7fe810f58fb0>; layer = 0x7fe810c51600>>
📍__31-[ViewController textFiledTest]_block_invoke_2 + 215🎈 x = 123
📍__31-[ViewController textFiledTest]_block_invoke241 + 211🎈 x = 0x7fe810c51a90; frame = (97.5 313.5; 180 40); text = '1231'; clipsToBounds = YES; opaque = NO; gestureRecognizers = 0x7fe810f58fb0>; layer = 0x7fe810c51600>>
📍__31-[ViewController textFiledTest]_block_invoke_2 + 215🎈 x = 1231
📍__31-[ViewController textFiledTest]_block_invoke241 + 211🎈 x = 0x7fe810c51a90; frame = (97.5 313.5; 180 40); text = '12312'; clipsToBounds = YES; opaque = NO; gestureRecognizers = 0x7fe810f58fb0>; layer = 0x7fe810c51600>>
📍__31-[ViewController textFiledTest]_block_invoke_2 + 215🎈 x = 12312
📍__31-[ViewController textFiledTest]_block_invoke241 + 211🎈 x = 0x7fe810c51a90; frame = (97.5 313.5; 180 40); text = '123123'; clipsToBounds = YES; opaque = NO; gestureRecognizers = 0x7fe810f58fb0>; layer = 0x7fe810c51600>>
📍__31-[ViewController textFiledTest]_block_invoke_2 + 215🎈 x = 123123
我们很容易的监听到textFiled中发生的变化,其中x的类型默认为id类型,我们已知它的类型的时候我们可以将其改变,就像上面代码,将id改成NSString类型。
手势
self.view.userInteractionEnabled = YES;
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
[[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
LxDBAnyVar(tap);
}];
[self.view addGestureRecognizer:tap];
为了方便,我们直接添加到self.view上,点击屏幕,得到打印结果:
📍__29-[ViewController gestureTest]_block_invoke + 184🎈 tap = 0x7fa2e3e1f9f0; state = Ended; view = 0x7fa2e3e20b70>; target= action=sendNext:, target=0x7fa2e3c064f0>)>>
通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil] subscribeNext:^(NSNotification * notification) {
LxDBAnyVar(notification);
}];
我们建立了一个通知,叫做进入后台,当程序进入后台的时候通知相应,当我们用RAC写通知的时候,我们有一个好处,就是不用removeObserve通知,因为RAC通知的监听者是RAC自己,它会帮你管理释放方法。可以看方法实现如下:
- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object {
@unsafeify(object);
return [[RACSignal createSignal:^(idRACSubscriber> subscriber) {
@strongify(object);
id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) {
[subscriber sendNext:note];
}];
return [RACDisposable disposableWithBlock:^{
[self removeObserver:observer];
}];
}] setNameWithFormat:@"-rac_addObserverForName: %@ object: ", notificationName, [object class], object];
}
定时器
//1. 延迟某个时间后再做某件事
[[RACScheduler mainThreadScheduler]afterDelay:2 schedule:^{
LxPrintAnything(rac);
}];
//2. 每间隔多长时间做一件事
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) {
LxDBAnyVar(date);
}];
这是定时器最常用的两种写法,第一种方法,延迟时间去做某件事情,更改afterDelay的属性。
第二种方法,每间隔多长时间做一件事情,更改interval属性。
代理
UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"RAC" message:@"ReactiveCocoa" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ensure", nil];
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * tuple) {
LxDBAnyVar(tuple);
LxDBAnyVar(tuple.first);
LxDBAnyVar(tuple.second);
LxDBAnyVar(tuple.third);
}];
[alertView show];
// 更简单的方式:
[[alertView rac_buttonClickedSignal]subscribeNext:^(id x) {
LxDBAnyVar(x);
}];
用RAC去写代理的时候,会有局限,只能取代没有返回值的代理方法,什么是没有返回值的代理呢?比如说tableView的代理方法:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath