這篇文章主要經過舉例說明Reactive Cocoa的使用方法,所舉的例子都比較典型和實用,在實際的項目中都會有所涉及,也但願你們能夠觸類旁通。服務器
一.輸入框輸入11位合法手機號,獲取驗證碼按鈕纔可用,號碼不合法,按鈕不可用,點擊按鈕,倒計時60s後,才能夠再次可用,在等待期間,不管輸入框輸入的是否再次合法,獲取驗證碼按鈕都是不可用的。函數
RACSignal *validPhone = [self.phoneTextField.rac_textSignal map:^id(NSString *text) {post
return @([RegFun checkPhoneLegal:text]);this
}];spa
self.sendCodeBtn.rac_command = [[RACCommand alloc] initWithEnabled: validPhone signalBlock:^RACSignal *(id input) {orm
return [self sendCodeSingal];server
}]; // validPhone控制點擊的block是否能夠執行,同時也控制了按鈕的狀態,當點擊後,只要block返回的signal尚未sendCompleted,這時候你不管怎麼輸入字符仍是作什麼操做,按鈕都是不可用的,這樣就解決了,正在請求接口,再輸入字符,按鈕又變成可用的問題。接口
-(RACSignal *)sendCodeSingal{圖片
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {get
[[AuthorizeURL sharedInstance] startWithM:URL_M_User andWithA:@"sendSmsCode" andOtherDic:dic andIsNeedWaitingView:YES andIsNeedPopMessageWhenSuccess:YES andIsNeedCallBlockWhenNoNetwork:YES andSuccessBlock:^(id responseObj, BOOL isSuccess) {
if (isSuccess) {
self.leftTime = 60;
RACSignal *sendCodeEnableSignal = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] take:self.leftTime]; //每1s執行一次
[sendCodeEnableSignal subscribeNext:^(id x) {
self.leftTime --;
[self.sendCodeBtn setTitle:[NSString stringWithFormat:@"等待(%d秒)",self.leftTime] forState:UIControlStateDisabled];
if (self.leftTime == 0) { //時間爲0時,才發送信號,讓按鈕可用。
[self.sendCodeBtn setTitle:@"發送驗證碼" forState:UIControlStateDisabled]; //字符歸位,否則下次就會顯示等待0.
[subscriber sendNext:@(isSuccess)];
[subscriber sendCompleted];
}
}];
}else{
[subscriber sendNext:@(isSuccess)];
[subscriber sendCompleted];
}
}];
return nil;
}];
}
二.經過代碼直接textView.text = @」this is a example」,也要達到rac_textSignal同樣的信號效果。
//直接給值的話,rac_textSignal是不調用的,必須經過觀察,而後,輸入的時候,觀察是不調用的,二者合併,有一者觸發便可。
RACSignal *validUserName = [[RACSignal merge:@[self.userNameTextField.rac_textSignal, RACObserve(self.userNameTextField, text)]] map:^id(NSString *text) {
return @(text.length > 0);
}];
三.很是解耦的控制底部tabbar小紅點和各個子小紅點的顯示和隱藏。
RACSignal *myMessageSignal = RACObserve(self, myMessageCircleNum);
RACSignal *groupMessageSignal = RACObserve(self, groupMessageNum);
RACSignal *mySysMessageSignal = RACObserve(self, mySysMessageCircleNum);
//這三個數值控制三個子小紅點的顯示隱藏,監聽他們值的改變,有改變,就發通知,去刷頁面,去控制子小紅點的顯示和隱藏。
[groupMessageSignal subscribeNext:^(NSNumber *x) {
[[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_CircleType object:@(8)]; //發送通知,其餘頁面只要有監聽,就能夠刷新頁面,和控制子其對應的子小紅點的顯示和隱藏。
[self saveToLocalWithType:8]; //存本地
}];
RACSignal *helloCircleSignal = [RACSignal combineLatest:@[myMessageSignal,mySysMessageSignal,groupMessageSignal] reduce:^id(NSNumber *myMessage,NSNumber *mySysMessage,NSNumber *groupMessage){
return @(myMessage.intValue == 0 && mySysMessage.intValue == 0 && groupMessage.intValue == 0); //當三者都爲0,證實他們對應的子小紅點都隱藏了,那麼底部的小紅點也才消失。
}];
[helloCircleSignal subscribeNext:^(NSNumber *x) {
XAppDelegate.homeVC.helloVCRedCircle.hidden = x.boolValue;
}]; //三者只要有一者的值改變,就會觸發這個合併的信號,就能夠刷新底部小紅點的顯示。
四.監聽登錄狀態的改變,從登錄到登出,從登出到登錄,狀態的改變須要刷新頁面和處理數據
//在登錄和登出的地方,會發出對應的通知,各個頁面只要監聽便可。
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:KNOTIFICATION_USERLOGINCHANGE object:nil] subscribeNext:^(NSNotification *notification) {
BOOL isinLogin = [notification.object boolValue];
if (isinLogin) { //從未登陸到登陸。
[self handleWhenLoginIn];
}else{ //從登陸到未登陸。
[self handleWhenLogout];
}
}];
五.替代各類delegate,讓代碼更集中,更易讀
UIActionSheet* sheet = [[UIActionSheet alloc]initWithTitle:nil delegate:nil cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"拍照",@"從相冊中選取", nil];
[[sheet rac_buttonClickedSignal] subscribeNext:^(NSNumber* number) { // UIActionSheet的delegate
UIImagePickerController* controller = nil;
int num = number.intValue;
if(num == 0) { //拍照
controller = [CameraAndPhoto getCameraPickerControllerAndIsFront:YES];
}
if(num == 1) { //相冊
controller = [CameraAndPhoto getPhotoLibarayPickerController];
}
if ((num == 0 || num == 1) && controller) {
[self presentViewController:controller animated:YES completion:nil];
[[controller rac_imageSelectedSignal] subscribeNext:^(NSDictionary *info) {
// UIImagePickerController點擊肯定後調用
[controller dismissViewControllerAnimated:YES completion:^{
UIImage *portraitImg = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
VPImageCropperViewController *imgCropperVC = [[VPImageCropperViewController alloc] initWithImage:portraitImg cropFrame:CGRectMake(0, (kScreen_Height - kScreen_Width)/2, kScreen_Width,kScreen_Width) limitScaleRatio:4.0 andIsNeedCircle:YES];
imgCropperVC.delegate = self; //delegate仍是要賦值的。
[[self rac_signalForSelector:@selector(imageCropper:didFinished:) fromProtocol:@protocol(VPImageCropperDelegate)] subscribeNext:^(RACTuple *tuple) { //圖片裁切VC的delegate,原本是要散落在self頁面,如今集成到這裏,圖片裁切肯定後的回調
[imgCropperVC dismissViewControllerAnimated:YES completion:^{
UIImage *editedImage = tuple.second;
}];
}];
[[self rac_signalForSelector:@selector(imageCropperDidCancel:) fromProtocol:@protocol(VPImageCropperDelegate)] subscribeNext:^(RACTuple *tuple){ //圖片裁切VC的delegate,圖片裁切取消後的回調
[imgCropperVC dismissViewControllerAnimated:YES completion:^{
}];
}];
[self presentViewController:imgCropperVC animated:YES completion:nil];
}];
} completed:^{ // UIImagePickerController點擊取消後調用
[controller dismissViewControllerAnimated:YES completion:^(){ //至關於cancel
}];
}];
}
}];
[sheet showInView:self.view];
六.信號混合使用,RACSubject的使用,將非RAC帶入RAC。
self.textSingal = [RACSubject subject]; //先聲明
self.publishBtn.rac_command = [[RACCommand alloc] initWithEnabled:self.textSingal signalBlock:^RACSignal *(id input) {
if (self.imageHasUploadToUpYun) {
return [self tellServerSignalWith:nil]; //圖片已經上傳成功了,若是告訴咱們的服務器失敗了,第二次點擊按鈕的時候,不用從新上傳圖片,直接將地址告訴咱們的服務器。
}else{
return [self submitSignal]; //開始上傳圖片
}
}];
[self.textSingal sendNext:[self isValid]]; //[self isValid]函數返回的NSNumber的值就是經過非RAC的普通代碼計算得來,來控制publishBtn的enable狀態,這句話能夠放在其餘須要控制按鈕狀態的地方,好比選擇圖片後,調用一下,初始化的使用調用一下。
//代碼的例子是先把圖片上傳到雲服務器,上傳成功後,再將取得的圖片地址告訴本身的服務器。
-(RACSignal *)submitSignal{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
self.uploadImageManager = [[UploadImageManager alloc] initWithMultiImageArray:self.multiPhotoVCManager.lastUploadImageArray andNeedWait:YES andMultiPhotoVCManager:self.multiPhotoVCManager andSuccessBlock:^(BOOL isAllComplete, NSArray *imageSrcArray, NSArray *hasSuccessObjArray, NSArray *hasFailedObjArray) {
if (imageSrcArray.count > 0) { //開始告訴咱們本身的服務器。
self.imageHasUploadToUpYun = YES; //這個時候,已經到雲了。除非再動圖片了,不然若是接下去告訴咱們本身的服務器失敗後,也不用再從新上傳圖片。
RACSignal *temp = [self tellServerSignalWith:subscriber]; //此時信號爲冷的,將上傳到雲的信號的subscriber傳遞到告訴服務器的函數,這樣才能在告訴服務器的信號完成後,也讓上傳到雲的信號可以完成,造成迴路。
[temp subscribeNext:^(id x) { //調用一下,激活告訴服務器的信號。
}];
}else{ //上傳完成了,一張都沒有成功。
[subscriber sendNext:@(0)];
[subscriber sendCompleted];
}
return nil;
}];
}
//將圖片地址告訴咱們本身的服務器
-(RACSignal *)tellServerSignalWith:(id<RACSubscriber>)subscriber1{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[AuthorizeURL sharedInstance] startWithM:URL_M_Daily andWithA:@"add" andOtherDic:dict andIsNeedWaitingView:YES andIsNeedPopMessageWhenSuccess:YES andIsNeedCallBlockWhenNoNetwork:YES andSuccessBlock:^(id responseObj, BOOL isSuccess) {
[subscriber sendNext:@(isSuccess)]; //讓告訴咱們服務器圖片地址的信號結束
[subscriber sendCompleted];
[subscriber1 sendNext:@(isSuccess)]; //讓雲服務器的信號結束
[subscriber1 sendCompleted];
}];
return nil;
}];
}