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

posted @ 2015-11-18 22:50  踏浪帅  阅读(546)  评论(0编辑  收藏  举报