ReactiveCocoa基础知识内容2
引用网络上一些实例的代码,针对ReactiveCocoa的运用可以更加有帮助;
1:跟AF结合时的写法,返回RACSignal
- (RACSignal *)fetchQuestionWithTag:(NSString *)tag { NSString *relativeURL = [NSString stringWithFormat:@"http://api.stackexchange.com/2.1/questions/?site=stackoverflow&order=desc&sort=hot&tagged=%@", tag]; RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { AFHTTPRequestOperation *operation = [ [AFHTTPRequestOperationManager manager] GET:relativeURL parameters:nil success:^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) { [subscriber sendNext:responseObject[@"items"]]; [subscriber sendCompleted]; } failure:^(AFHTTPRequestOperation *operation, NSError *error){ [subscriber sendError:error]; }]; return [RACDisposable disposableWithBlock:^{ [operation cancel]; }]; }]; return signal; }
2:RACCommand的运用,作为事件响应
定义一个ViewModel @interface SubscribeViewModel : NSObject @property(nonatomic, strong) RACCommand *subscribeCommand; // write to this property @property(nonatomic, strong) NSString *email; // read from this property @property(nonatomic, strong) NSString *statusMessage; @end
@property(nonatomic, strong) RACSignal *emailValidSignal; 实现代码: - (RACCommand *)subscribeCommand { if (!_subscribeCommand) { @weakify(self); _subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) { @strongify(self); return [SubscribeViewModel postEmail:self.email]; }]; } return _subscribeCommand; } + (RACSignal *)postEmail:(NSString *)email { AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.requestSerializer = [AFJSONRequestSerializer new]; NSDictionary *body = @{@"email": email ?: @""}; return [[[manager rac_POST:kSubscribeURL parameters:body] logError] replayLazily]; } - (RACSignal *)emailValidSignal { if (!_emailValidSignal) { _emailValidSignal = [RACObserve(self, email) map:^id(NSString *email) { return @([email isValidEmail]); }]; } return _emailValidSignal; }
调用绑定: - (void)bindWithViewModel { RAC(self.viewModel, email) = self.emailTextField.rac_textSignal; self.subscribeButton.rac_command = self.viewModel.subscribeCommand; RAC(self.statusLabel, text) = RACObserve(self.viewModel, statusMessage); } - (UITextField *)emailTextField { if (!_emailTextField) { _emailTextField = [UITextField new]; _emailTextField.borderStyle = UITextBorderStyleRoundedRect; _emailTextField.font = [UIFont boldSystemFontOfSize:16]; _emailTextField.placeholder = NSLocalizedString(@"Email address", nil); _emailTextField.keyboardType = UIKeyboardTypeEmailAddress; _emailTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; } return _emailTextField; } - (UIButton *)subscribeButton { if (!_subscribeButton) { _subscribeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [_subscribeButton setTitle:NSLocalizedString(@"Subscribe", nil) forState:UIControlStateNormal]; } return _subscribeButton; } - (UILabel *)statusLabel { if (!_statusLabel) { _statusLabel = [UILabel new]; } return _statusLabel; }
说明:RACCommand类用于表示事件的执行,一般来说是在UI上的某些动作来触发这些事件,比如点击一个按钮。RACCommand的实例能够决定是否可以被执行,这个特性能反应在UI上,而且它能确保在其不可用时不会被执行。通常,当一个命令可以执行时,会将它的属性allowsConcurrentExecution设置为它的默认值:NO,从而确保在这个命令已经正在执行的时候,不会同时再执行新的操作。命令执行的返回值是一个RACSignal,因此我们能对该返回值进行next:,completed或error:,
3:关于RACObserve的用法
监听对象的成员变量变化,当成员变量值被改变时,触发做一些事情。 这种情况其实就是IOS KVO机制使用的场景,使用KVO实现,通常有三个步骤:1,给对象的成员变量添加监听;2,实现监听回调;3,取消监听;而通过RAC可以直接实现,RAC的回调是通过block实现的,类似于过程式编程,上下文也更容易理解一些。
a: 场景:当前类有一个成员变量 NSString *input,当它的值被改变时,发送一个请求。
[RACObserve(self, input) subscribeNext:^(NSString* x){ request(x);//发送一个请求 }];
说明:每次input值被修改时,就会调用此block,并且把修改后的值做为参数传进来。
b:场景:在上面场景中,当用户输入的值以2开头时,才发请求.
[[RACObserve(self, input) filter:^(NSString* value){ if ([value hasPrefix:@"2"]) { return YES; } else { return NO; } }] subscribeNext:^(NSString* x){ request(x);//发送一个请求 }];
c:上面场景是监听自己的成员变量,如果想监听UITextField输入值变化,框架也做了封装可以代替系统回调
[[self.priceInput.rac_textSignal filter:^(NSString *str) { if (str.integerValue > 20) { return YES; } else { return NO; } }] subscribeNext:^(NSString *str) { request(x);//发送一个请求 }];
d:场景:button监听 两个输入框有值和一个成员变量值,当输入框有输入且成员变量为真时,button为可点击状态
RAC(self.payButton,enabled) = [RACSignal combineLatest:@[self.priceInput.rac_textSignal, self.nameInput.rac_textSignal, RACObserve(self, isConnected) ] reduce:^(NSString *price, NSString *name, NSNumber *connect){ return @(price.length > 0 && name.length > 0 && [connect boolValue]); }];
说明:同时监听多个变量变化,当这些变量满足一定条件时,使button为可点击状态
e:场景:满足上面条件时,直接发送请求
[[RACSignal combineLatest:@[self.priceInput.rac_textSignal, self.nameInput.rac_textSignal, RACObserve(self, isConnected) ] reduce:^(NSString *price, NSString *name, NSNumber *connect){ return @(price.length > 0 && name.length > 0 && ![connect boolValue]); }] subscribeNext:^(NSNumber *res){ if ([res boolValue]) { NSLog(@"XXXXX send request"); } }];
f:distinctUntilChanged直到收到不同值才响应,可以过滤掉那些不必要的网络请求等
@weakify(self); //Start Binding our properties RAC(self.nameField,text) = [RACObserve(self.viewModel, playerName) distinctUntilChanged]; [[self.nameField.rac_textSignal distinctUntilChanged] subscribeNext:^(NSString *x) { //this creates a reference to self that when used with @weakify(self); //makes sure self isn't retained @strongify(self); self.viewModel.playerName = x; }];
4:RACScheduler为RAC调度类(主线程,子线程等)
显示网络下载的图片
RAC(self.imageView, image) = [[RACSignal startEagerlyWithScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground] block:^(id <RACSubscriber> subscriber) { NSError *error; NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://ww3.sinaimg.cn/bmiddle/7128be06jw1ei4hfthoj3j20hs0bomyd.jpg"] options:NSDataReadingMappedAlways error:&error]; if(error) { [subscriber sendError:error]; } else { [subscriber sendNext:[UIImage imageWithData:data]]; [subscriber sendCompleted]; } }] deliverOn:[RACScheduler mainThreadScheduler]];
说明:这段代码会在后台线程立即发起一个请求,然后传递到主线程上更新UI,主线程上执行[RACScheduler mainThreadScheduler],
信号传递:- (RACSignal *)deliverOn:(RACScheduler *)scheduler
5:控件结合的实例
a:对uibutton添加了一个rac_signalForControlEvents的方式,就不用利用addtarget的方式来再写一个方法来进行对uibutton添加点击事件了。
[[self.testBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { TestViewController *testVC = [[TestViewController alloc] init]; [self.navigationController pushViewController:testVC animated:YES]; }];
b:在UIAlertView的使用
UIAlertView *chooseAlert = [[UIAlertView alloc] initWithTitle:@"选择图片上传"message:nil delegate:nil cancelButtonTitle:@"取消"otherButtonTitles:@"拍照上传", @"从相册选择", nil]; [chooseAlert show]; [[chooseAlert rac_buttonClickedSignal] subscribeNext:^(NSNumber *indexNumber) { if ([indexNumber intValue] == 1) { [self chooseFromCamera]; } else if ([indexNumber intValue] == 2) { [self chooseFromAlbum]; } }];
c:在UITextfield的使用,self是因为我继承了一个textfield先进行功能添加和封装。所以self就是代指一个textfield了。
[[self rac_signalForControlEvents:UIControlEventEditingDidBegin] subscribeNext:^(NSNumber *editing) { self.bottomBorder.backgroundColor = [UIColor blackColor].CGColor; }]; [[self rac_signalForControlEvents:UIControlEventEditingDidEnd] subscribeNext:^(NSNumber *editing) { self.bottomBorder.backgroundColor = [UIColor grayColor].CGColor; }];
说明:当选择这个框的时候,线会加粗变黑
d:监控UIPagecontrol改变
[RACObserve(self.imagePlayer.pageControl, currentPage) subscribeNext:^(id x) {
[self refreshSlideContent:self.imagePlayer.pageControl.currentPage];
}];
不错的实例代码:
ReactiveCocoa实用案例:http://www.saitjr.com/ios/ios-reactivecocoa-utility-demo.html