Reactive Cocoa实践举例
2019獨角獸企業重金招聘Python工程師標準>>>
這篇文章主要通過舉例說明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) {?? // 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;
}];
}
轉載于:https://my.oschina.net/u/172808/blog/523405
總結
以上是生活随笔為你收集整理的Reactive Cocoa实践举例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mongodb基本操作之.net
- 下一篇: MATLAB 人脸定位