十一岁的加重songxing10000…… ------ 回到博主首页

项目中学习ReactiveCocoa的使用方法

一、注册控制器

控制器上的一个属性

@property (weak, nonatomic) IBOutlet UIBarButtonItem *signInBtn;

viewDidLoad

方法中写入

self.signInBtn.rac_command = self.viewModel.executeSignInCommand;

self.viewModel是控制器上的一个viewModel

@property (nonatomic, weak) MSFAuthorizeViewModel *viewModel;

其上有个属性

@property (nonatomic, strong) RACCommand *executeSignInCommand;

这个executeSignInCommand在viewModel的初始化方法中生成

- (instancetype)initWithServices:(id <MSFViewModelServices>)services {
    
   self = [super init];
    if (!self) {
        return nil;
    }
    _services = services;

       .....
   
       _executeSignInCommand = [[RACCommand alloc]      
        initWithSignalBlock:^RACSignal *(id input) {
        
               self.loginType = MSFLoginSignIn;
        [self.services presentViewModel:self];
        return [RACSignal return:nil];
    }];
       ....
}

而presentViewModel:仅仅只是一个协议,没有实现,有个样本

- (void)presentViewModel:(id)viewModel {
    id viewController;
    
    if ([viewModel isKindOfClass:MSFAuthorizeViewModel.class]) {
        MSFAuthenticateViewController *loginViewController = [[MSFAuthenticateViewController alloc] initWithViewModel:viewModel];
        viewController = [[UINavigationController alloc] initWithRootViewController:loginViewController];
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(UIScreen.mainScreen.bounds), 20)];
        view.backgroundColor = UIColor.blackColor;
        [[(UIViewController *)viewController view] addSubview:view];
    } else {
        NSLog(@"an unknown ViewModel was present!");
    }
    
    [self.navigationController presentViewController:viewController animated:YES completion:nil];
}

再看看控制器的其他属性

@property (nonatomic, weak) IBOutlet UITextField *usernameF;
@property (nonatomic, weak) IBOutlet UITextField *captchaF;
@property (nonatomic, weak) IBOutlet UITextField *passwordF;

也是在viewDidLoad中处理的,方法viewWillAppear有一执行就会有一个信号发送,控制器订阅了这个信号,并在收到信号后,执行操作:

把控件上的文件赋值给viewModel上的对应的属性。

     @weakify(self)
    [[self rac_signalForSelector:@selector(viewWillAppear:)] subscribeNext:^(id x) {
        @strongify(self)
        self.viewModel.username = self.usernameF.text;
        self.viewModel.password = self.passwordF.text;
        self.viewModel.loginType = MSFLoginSignUp;
    }];

当然,相应的textField文本改变时,我们也会处理

输入的文本长度超过规定的长度,则进行截取然后放置到field中,再赋值给viewModel中对应的属性

[self.username.rac_textSignal subscribeNext:^(id x) {
        @strongify(self)
        if ([x length] > MSFAuthorizeUsernameMaxLength) self.username.text = [x substringToIndex:MSFAuthorizeUsernameMaxLength];
        self.viewModel.username = self.username.text;
    }];

密码输入框也是

[self.password.rac_textSignal subscribeNext:^(id x) {
        @strongify(self)
        if ([x length] > MSFAuthorizePasswordMaxLength) self.password.text = [x substringToIndex:MSFAuthorizePasswordMaxLength];
        self.viewModel.password = self.password.text;
    }];

还有密码field在按下下一步的return键时,就相当于按下了注册按钮,此时就会调用viewModel上的executeSignUp命令

[self.password.rac_keyboardReturnSignal subscribeNext:^(id x) {
        @strongify(self)
        [self.viewModel.executeSignUp execute:nil];
    }];

这个命令也是跟登录命令一样在同一个方法中初始化

_executeSignUp = [[RACCommand alloc] initWithEnabled:self.signUpValidSignal
        signalBlock:^RACSignal *(id input) {
            @strongify(self)
         
            return [self executeSignUpSignal];
        }];

当然,这个命令要受到能点不能点击的影响self.signUpValidSignal,当用户名和密码还有验证码都有输入的情况下,就会调用自身上的一个excuteSignUpSignal

- (RACSignal *)signUpValidSignal {
    return [RACSignal
        combineLatest:@[
            RACObserve(self, username),
            RACObserve(self, password),
            RACObserve(self, captcha),
        ]
        reduce:^id(NSString *username, NSString *password, NSString *captcha){
            return @(username != nil && password != nil && captcha != nil);
        }];
}

突然间感觉这里好复杂不再深究,先看其他的

- (RACSignal *)executeSignUpSignal {
    if (![self.username isMobile]) {
        return [RACSignal error:[self.class errorWithFailureReason:@"请填写真实的手机号码"]];
    } else if (![self.password isPassword]) {
        return [RACSignal error:[self.class errorWithFailureReason:@"请填写8到16位数字和字母组合的密码"]];
    } else if (![self.captcha isCaptcha]) {
        return [RACSignal error:[self.class errorWithFailureReason:@"请填写验证码"]];
    } else if (!self.agreeOnLicense) {
        return [RACSignal error:[self.class errorWithFailureReason:@"请阅读注册协议"]];
    }
    
    MSFUser *user = [MSFUser userWithServer:MSFServer.dotComServer];
    return [[MSFClient
        signUpAsUser:user password:self.password phone:self.username captcha:self.captcha]
        doNext:^(MSFClient *client) {
            _signInValid = YES;
            [self.services setHttpClient:client];
            [[client fetchUserInfo] subscribeNext:^(MSFUser *x) {
                [client.user mergeValueForKey:@keypath(x.personal) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.professional) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.contacts) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.profiles) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.insurance) fromModel:x];
            }];
        }];
}

有个显示密码与隐藏密码的按钮

[[self.showPasswordButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        @strongify(self)
        self.showPasswordButton.selected = !self.showPasswordButton.selected;
        NSString *text = self.password.text;
        self.password.text = text;
        self.password.enabled = NO;
        [self.password setSecureTextEntry:!self.showPasswordButton.selected];
        self.password.enabled = YES;
        [self.password becomeFirstResponder];
    }];

再看看验证码输入框,长度限制、截取、同步viewModel的对应属性

[self.captcha.rac_textSignal subscribeNext:^(id x) {
        @strongify(self)
        if ([x length] > MSFAuthorizeCaptchaMaxLength) self.captcha.text = [x substringToIndex:MSFAuthorizeCaptchaMaxLength];
        self.viewModel.captcha = self.captcha.text;
    }];

同步viewModel上的命令

    self.sendCaptchaButton.rac_command = self.viewModel.executeCaptcha;

执行命令时的操作,键盘释放,SVProgressHUD显示正在获取验证码

[self.sendCaptchaButton.rac_command.executionSignals subscribeNext:^(RACSignal *captchaSignal) {
        @strongify(self)
        [self.view endEditing:YES];
        [SVProgressHUD showWithStatus:@"正在获取验证码" maskType:SVProgressHUDMaskTypeClear];
        [captchaSignal subscribeNext:^(id x) {
            [SVProgressHUD dismiss];
        }];
    }];

当这个命令有错误时的回调

[self.sendCaptchaButton.rac_command.errors subscribeNext:^(NSError *error) {
        [SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedFailureReasonErrorKey]];
    }];

同理,提交、注册按钮也是类似 的,先绑定命令

    self.commitButton.rac_command = self.viewModel.executeSignUp;

然后,执行命令的回调,释放键盘,提示正在注册...然后会发送一个通知,没看到这个是什么意思 [signUpSignal subscribeNext:^(id x) {

 

[self.commitButton.rac_command.executionSignals subscribeNext:^(RACSignal *signUpSignal) {
        @strongify(self)
        [self.view endEditing:YES];
        [SVProgressHUD showWithStatus:@"正在注册..." maskType:SVProgressHUDMaskTypeClear];
        [signUpSignal subscribeNext:^(id x) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"MSFREQUESTCONTRACTSNOTIFACATION" object:nil];
            [SVProgressHUD dismiss];
        }];
    }];

收到错误时的信号

[self.commitButton.rac_command.errors subscribeNext:^(NSError *error) {
        [SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedFailureReasonErrorKey]];
    }];
    

验证码倒计时Label

@property (nonatomic, weak) IBOutlet UILabel *counterLabel;

viewDidLoad中操作,viewModel上的对应的属性变更时,会同步到这个控制器上的label来

    RAC(self, counterLabel.text) = RACObserve(self, viewModel.counter);

 

 

 

 

 

posted @ 2017-03-23 14:20  songxing10000  阅读(886)  评论(0编辑  收藏  举报