Reactive Cocoa实践举例

本文通过多个实际案例介绍ReactiveCocoa的使用方法,包括输入验证、代码直接赋值的信号转换、解耦控制小红点显示、状态变化监听及替代委托模式等。

这篇文章主要通过举例说明Reactive Cocoa的使用方法,所举的例子都比较典型和实用,在实际的项目中都会有所涉及,也希望大家可以举一反三。

 

 

一.输入框输入11位合法手机号,获取验证码按钮才可用,号码不合法,按钮不可用,点击按钮,倒计时60s后,才可以再次可用,在等待期间,无论输入框输入的是否再次合法,获取验证码按钮都是不可用的。

 

RACSignal *validPhone = [self.phoneTextField.rac_textSignal map:^id(NSString *text) {

        return @([RegFun checkPhoneLegal:text]);

}];

 

self.sendCodeBtn.rac_command = [[RACCommand alloc] initWithEnabled: validPhone signalBlock:^RACSignal *(id input) {

        return [self sendCodeSingal];

}];  // validPhone控制点击的block是否可以执行,同时也控制了按钮的状态,当点击后,只要block返回的signal还没有sendCompleted,这时候你无论怎么输入字符还是做什么操作,按钮都是不可用的,这样就解决了,正在请求接口,再输入字符,按钮又变成可用的问题。

 

-(RACSignal *)sendCodeSingal{

return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        [[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) {   // UIActionSheetdelegate

        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) {  //图片裁切VCdelegate,本来是要散落在self页面,现在集成到这里,图片裁切确定后的回调

                        [imgCropperVC dismissViewControllerAnimated:YES completion:^{

                            UIImage *editedImage = tuple.second;

                           }];

}];

                    [[self rac_signalForSelector:@selector(imageCropperDidCancel:) fromProtocol:@protocol(VPImageCropperDelegate)] subscribeNext:^(RACTuple *tuple){  //图片裁切VCdelegate,图片裁切取消后的回调

                        [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的普通代码计算得来,来控制publishBtnenable状态,这句话可以放在其他需要控制按钮状态的地方,比如选择图片后,调用一下,初始化的使用调用一下。

 

//代码的例子是先把图片上传到云服务器,上传成功后,再将取得的图片地址告诉自己的服务器。

-(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;

}];

}


转载于:https://my.oschina.net/u/172808/blog/523405

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值