使用ReactiveCocoa响应用户输入

时间:2014-06-11 20:59:10

标签: ios binding reactive-cocoa

我正在尝试使用ReactiveCocoa来控制应用中文本字段的绑定和验证。当我订阅信号时,它会立即从文本字段到模型进行绑定并运行验证。通常这不是问题,但在这种情况下,该字段是“密码”输入,其中模型的初始值不会被复制到文本字段。我希望绑定和验证仅在用户实际在字段中键入内容时触发。有谁知道这样做的方法?

以下是我目前正在做的事情:

- (void)setUpBindings: forModel:(NSObject<ValidationModel> *)model {
        NSString *property = @"password"
        NSInteger throttleTime = 1.5;

        [[[self.textField.rac_textSignal distinctUntilChanged]
          throttle:throttleTime]
         subscribeNext:^(id x) {
             NSLog([NSString stringWithFormat:@"Model: %@, Value: %@", [model valueForKey:property], x]);
             [model setValue:x forKey:property];
         }];

        [self bindValidator:[model.validators objectForKey:property]];

}

- (RACSignal *) passwordIsValid {
    @weakify(self);
    return [[RACObserve(self,password) distinctUntilChanged]
            map:^id (NSString *newPassword) {
                @strongify(self);
                NSArray *errors = [self validatePassword];
                return errors;
            }];
}

 -(void)bindValidator:(RACSignal *)validator
{    
    if(validator != nil)
    {
        [[[validator doNext:^(NSArray *errors) {
                                   if(errors.count > 0)
                                   {
                                       NSError *error = [errors firstObject];
                                       self.errorString =error.localizedDescription;  
                                   }
                                   else
                                   {
                                       self.errorString = @"";
                                   }
                               }]
                              map:^id(NSArray *errors) {
                                  return errors.count <=0 ? @(1) : nil;
                              }] subscribeNext:^(id x) {
                                  self.isValid = !!x;
                              }];
    }
}

2 个答案:

答案 0 :(得分:0)

我找到了一个可能的解决方案。 ReactiveCocoa有一个rac_signalForControlEvents方法,允许您手动指定要观察的事件。使用它,我能够为我的文本字段的UIControlEventEditingChanged事件定义一个初始信号。然后,我在subscribeNext中移动了我的绑定和验证信号的设置,延迟了他们的订阅,直到从文本字段发送更改事件。我在OP中的设置方法如下所示:

- (void)setUpBindings:(NSString *)property forModel:(NSObject<ValidationModel> *)model {

        NSInteger throttleTime = 1.5;    
        RACSignal *textChangeSignal = [[self.textField.rac_textSignal distinctUntilChanged]
          throttle:throttleTime];

        //wait to subscribe to the signal until the user actually makes changes to the field.
        //the 'take:1' call ensures that the subscription only happens the first time the event
        //is observed.
        [[[self.textField rac_signalForControlEvents:UIControlEventEditingChanged]
          take:1]
          subscribeNext:^(id x) {

            [self bindValidator:[model.validators objectForKey:self.reuseIdentifier]];
            [textChangeSignal subscribeNext:^(id x) {
                [model setValue:x forKey:property];
            }];
        }];   
}

重要提示:请注意&#39;拍摄:1&#39;外链中的方法。你必须包括这个。没有那个电话,外部&#39; subscribeNext&#39;每次触发编辑事件时都会运行,从而导致同一目标和事件的多个订阅者。有关详细信息,请参阅:How do I create a ReactiveCocoa subscriber that receives a signal only once, then unsubscribes/releases itself?

我现在要打开这个。这种方式有效,但我确信必须有一种更清洁的方法。

答案 1 :(得分:0)

您可以使用以下内容:

@weakify(self);
RACSignal *validPasswordSignal = [self.passwordTextField.rac_textSignal map:^id(NSString *text) {
    @strongify(self);
    return @([self isValidPassword:text]);
}];

- (BOOL)isValidPassword:(NSString *)password
{
    return ([password length] > 0);
}

您可以将isValidPassword中的条件更改为您想要的任何内容。