简体   繁体   English

使用Reactive Cocoa组合多个信号不起作用

[英]Combining multiple signal not working using Reactive Cocoa

I am following this tutorial: http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1 . 我正在学习本教程: http//www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1 It is working for username and password field. 它适用于用户名和密码字段。

I am trying to create sign up form which contains three fields: email, password, confirmPassword. 我正在尝试创建包含三个字段的注册表单:email,password,confirmPassword。

I have validated email address (working perfectly) I have validated password like this: 我已经验证了电子邮件地址(工作正常)我已经验证了这样的密码:

password should be at least 6 characters and both password and confirmPassword must be same. 密码至少应为6个字符,密码和confirmPassword必须相同。

Here is my code: 这是我的代码:

RACSignal *validEmailSignal =
[emailTextField.rac_textSignal
 map:^id(NSString *text) {
     return @([text isValidEmailAddress]);
 }];

RACSignal *validPasswordSignal =
[passwordTextField.rac_textSignal
 map:^id(NSString *text) {
     return @([text isEqualToString:self.confirmPasswordTextField.text] && text.length >5);
 }];

RACSignal *validConfirmPasswordSignal =
[confirmPasswordTextField.rac_textSignal
 map:^id(NSString *text) {
     return @([text isEqualToString:self.passwordTextField.text] && text.length >5 );
 }];

[[validEmailSignal
  map:^id(NSNumber *emailValid) {
      return [emailValid boolValue] ? [UIColor greenColor] : [UIColor redColor];
  }]
 subscribeNext:^(UIColor *color) {
     emailTextField.layer.borderColor=[color CGColor];
     emailTextField.layer.borderWidth= 1.0f;

 }];

[[validPasswordSignal
  map:^id(NSNumber *passwordValid) {
      return [passwordValid boolValue] ? [UIColor greenColor] : [UIColor redColor];
  }]
 subscribeNext:^(UIColor *color) {
     passwordTextField.layer.borderColor=[color CGColor];
     passwordTextField.layer.borderWidth= 1.0f;
     confirmPasswordTextField.layer.borderColor=[color CGColor];
     confirmPasswordTextField.layer.borderWidth= 1.0f;
 }];

[[validConfirmPasswordSignal
  map:^id(NSNumber *passwordValid) {
      return [passwordValid boolValue] ? [UIColor greenColor] : [UIColor redColor];
  }]
 subscribeNext:^(UIColor *color) {
     passwordTextField.layer.borderColor=[color CGColor];
     passwordTextField.layer.borderWidth= 1.0f;
     confirmPasswordTextField.layer.borderColor=[color CGColor];
     confirmPasswordTextField.layer.borderWidth= 1.0f;
 }];


RACSignal *signUpActiveSignal =
[RACSignal combineLatest:@[validEmailSignal, validPasswordSignal, validConfirmPasswordSignal]
                  reduce:^id(NSNumber *emailValid, NSNumber *passwordValid, NSNumber *confirmPasswordValid) {
                      return @([emailValid boolValue] && [passwordValid boolValue] && [confirmPasswordValid boolValue]);
                  }];


[signUpActiveSignal subscribeNext:^(NSNumber *signupActive) {

    NSLog(@"sign up button enabled : %hhd", [signupActive boolValue]);
    _signUpButton.enabled = [signupActive boolValue];
}];

Here, if I take only emailValid's value, it is sending 1 when valid. 在这里,如果我只接受emailValid的值,则在有效时发送1。 But if I include all three values, it always returns 0 even if all are valid. 但是如果我包含所有三个值,即使所有值都有效,它也总是返回0。

Here are snap shots of the simulator: 以下是模拟器的快照:

在此输入图像描述在此输入图像描述

I just wanted to enable "Sign up" button when all three fields are valid. 我只想在所有三个字段都有效时启用“注册”按钮。 I also want disable "Go" button on the keyboard till validation success. 我还想在键盘上禁用“开始”按钮,直到验证成功。

Okay, two things: 好的,有两件事:

You can use the and helper of RACSignal to make that signal reduction a little cleaner: 您可以使用RACSignaland帮助RACSignal信号减少更清洁:

RACSignal *signUpActiveSignal = [[RACSignal combineLatest:@[validEmailSignal,
                                                            validPasswordSignal,
                                                            validConfirmPasswordSignal]] and];

You can also use the RAC macro to bind the enabled property (the only reason that the example code is using subscribeNext instead of RAC is that CGColorRef isn't of type id ): 您还可以使用RAC宏来绑定enabled属性(示例代码使用subscribeNext而不是RAC的唯一原因是CGColorRef不是id类型):

RAC(self.signUpButton, enabled) = signUpActiveSignal;

Now onto the reason this isn't working. 现在说这不起作用的原因。 Let's look at the calculations for the password fields: 我们来看看密码字段的计算:

RACSignal *validPasswordSignal = [passwordTextField.rac_textSignal map:^id(NSString *text) {
    return @([text isEqualToString:self.confirmPasswordTextField.text] && text.length > 5);
}];

RACSignal *validConfirmPasswordSignal = [confirmPasswordTextField.rac_textSignal map:^id(NSString *text) {
    return @([text isEqualToString:self.passwordTextField.text] && text.length > 5);
}];

Every time passwordTextField 's text changes, it compares it to confirmPasswordTextField . 每次passwordTextField的文本发生更改时,都会将其与confirmPasswordTextField进行比较。 But it only checks that they're the same when passwordTextField changes, not when confirmPasswordTextField changes. 但它只会在passwordTextField更改时检查它们是否相同, 而不是confirmPasswordTextField更改时。 So let's imagine the following scenario, ignoring (for now) the length requirement: 所以让我们想象下面的场景,忽略(现在)长度要求:

------------------- initial state -------------------

           -------------
password: |             |   passwordValid: YES
           -------------

           -------------
 confirm: |             |   confirmValid: YES
           -------------

-------------- user types in a password --------------

           -------------
password: | asdf        |   passwordValid: NO
           -------------

           -------------
 confirm: |             |   confirmValid: YES (this will only recalculate when the confirm field changes, not when the password field changes)
           -------------


------- user types in the password confirmation -------

           -------------
password: | asdf        |   passwordValid: NO (this didn't recalculate because the contents of the password field didn't change)
           -------------

           -------------
 confirm: | asdf        |   confirmValid: YES
           -------------

Aha! 啊哈! So we need to recalculate when either of them changes. 因此,我们需要在其中任何一个发生变化时重新计算。 And, really, that should be a separate signal from the lengths: 而且,实际上,这应该是与长度分开的信号:

RACSignal *passwordsMatch = [RACSignal combineLatest:@[passwordTextField.rac_textSignal,
                                                       confirmPasswordTextField.rac_textSignal]
                                              reduce:(NSString *password, NSString *confirm) {
                                                  return @([password isEqualToString:confirm]);
                                              }];

RACSignal *isPasswordLongEnough = [passwordTextField.rac_textSignal map:^(NSString *text) {
    return @(text.length >= 6);
}];

Then we can put it all together: 然后我们可以把它们放在一起:

RACSignal *signUpActiveSignal = [[RACSignal combineLatest:@[validEmailSignal,
                                                            isPasswordLongEnough,
                                                            passwordsMatch]] and];

Which is great, because that's sort of how we think of it: "You can sign in when the email address is valid, the password you entered is long enough, and the password confirm field matches the password." 这很好,因为我们就是这样想的:“当电子邮件地址有效时,您可以登录,输入的密码足够长,密码确认字段与密码匹配。” Declarative! 声明!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM