如何使用ReactiveCocoa从第一个移动到最后一个UITextField

时间:2014-09-01 16:54:40

标签: ios objective-c reactive-programming reactive-cocoa

我想在我工作的新项目中使用ReactiveCocoa,没什么好看的,因为项目总是尽快但我想在实践中开始学习这个很酷的框架。我有一个理想的实现,因为我认为ReactiveCocoa很简单:

enter image description here

因此,在用户将一个数字插入第一个字段后,第二个字段应该变为活动状态,然后是第三个等等。我写了这个代码:

_code = [[NSMutableArray alloc] initWithCapacity:6];

[[_field0.rac_textSignal filter:^BOOL(NSString *value) {
    if ([value length] == 1){
        return TRUE;
    }
    return FALSE;

}] subscribeNext:^(id x) {
    _code[0] = x;
    [_field1 becomeFirstResponder];
}];

[[_field1.rac_textSignal filter:^BOOL(NSString *value) {
    if ([value length] == 1){
        return TRUE;
    }
    return FALSE;

}] subscribeNext:^(id x) {
    _code[1] = x;
    [_field2 becomeFirstResponder];
}];

[[_field2.rac_textSignal filter:^BOOL(NSString *value) {
    if ([value length]  == 1){
        return TRUE;
    }
    return FALSE;

}] subscribeNext:^(id x) {
    _code[2] = x;
    [_field3 becomeFirstResponder];
}];

[[_field3.rac_textSignal filter:^BOOL(NSString *value) {
    if ([value length] == 1){
        return TRUE;
    }
    return FALSE;

}] subscribeNext:^(id x) {
    _code[3] = x;
    [_field4 becomeFirstResponder];
}];

[[_field4.rac_textSignal filter:^BOOL(NSString *value) {
    if ([value length] == 1){
        return TRUE;
    }
    return FALSE;

}] subscribeNext:^(id x) {
    _code[4] = x;
    [_field5 becomeFirstResponder];
}];

[[_field5.rac_textSignal filter:^BOOL(NSString *value) {
    if ([value length] == 1){
        return TRUE;
    }
    return FALSE;

}] subscribeNext:^(id x) {
    _code[5] = x;
}];

当我第一次插入字符时它工作正常但是当我完成时例如再次选择第一个字段事情变得混乱 - 休息字段正在重置,我有时可以插入更多1个字符等。

我想我错过了一些基本的东西:)

1 个答案:

答案 0 :(得分:0)

这是一种实现你想要的东西的方法,我想,更多的是ReactiveCocoa的精神。请记住,我对ReactiveCocoa仍然相当新,所以可能有一种更简单的方法来做到这一点。首先创建一些字段:

UITextField *field1 = [[UITextField alloc] initWithFrame:CGRectMake(0, 200, 200, 44)];
UITextField *field2 = [[UITextField alloc] initWithFrame:CGRectMake(0, 260, 200, 44)];
UITextField *field3 = [[UITextField alloc] initWithFrame:CGRectMake(0, 320, 200, 44)];

[self.view addSubview:field1];
[self.view addSubview:field2];
[self.view addSubview:field3];

field1.placeholder = @"field1";
field2.placeholder = @"field2";
field3.placeholder = @"field3";

NSArray *fields = @[field1, field2, field3];

现在,我们将为这些字段构建一些频道供以后使用。请务必查看RACChannel. RACSequence *channels = [fields.rac_sequence map:^id(UITextField *field) { RACChannel *channel = [RACChannel new]; RACChannelTerminal *terminal = [field rac_newTextChannel]; [terminal subscribe:channel.leadingTerminal]; [channel.leadingTerminal subscribe:terminal]; return channel; }]; documentation这些字段绑定到各自的文本字段'信道。

data

现在,假设我们的数据在频道上正确显示,让我们收集结果,并绑定到我们的combineLatest属性。对RACTuple.的调用将等待产生一个值,直到所有通道终端产生至少一个值,然后将为产生新值的任何通道终端产生值。这些值被收集到RAC(self, data) = [[[RACSignal combineLatest:[channels map:^id(RACChannel *channel) { return channel.leadingTerminal; }]] map:^id(id value) { return [value rac_sequence].array; }] doNext:^(NSArray *contents) { [contents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSLog(@"Contents at index %ld: %@", idx, obj); }]; }]; 然后我们将它转​​换为一个序列,最后是一个数组,因为这就是我们绑定到的属性。为方便起见,我打印出结果。

switchToLatest

现在,为了处理字段之间的切换并更新它们的值,让我们产生一个信号信号。首先,让我们用各自的字段压缩频道。对于每个,我们返回一个等待字段开始编辑的信号,并在发生时返回通道和字段的元组。我们合并这些信号,然后在末尾调用RACSignal *latestEditing = [[RACSignal merge:[[channels zipWith:fields.rac_sequence] map:^id(RACTuple *tuple) { RACTupleUnpack(__unused RACChannel *channel, UITextField *field) = tuple; return [[field rac_signalForControlEvents:UIControlEventEditingDidBegin] map:^id(id value) { return [RACSignal return:tuple]; }]; }]] switchToLatest]; ,以便我们可以获得最新的内部信号以供下面使用。

latestEditing

当我们订阅UIControlEventEditingDidEnd信号时,我们会在字段编辑时收到通知。一旦发生这种情况,我们希望等待与文本字段对应的通道终端上的传入值。请注意,我们订阅信号直到收到[latestEditing subscribeNext:^(RACTuple *tuple) { RACTupleUnpack(RACChannel *channel, __unused UITextField *field) = tuple; [[channel.followingTerminal takeUntil:[field rac_signalForControlEvents:UIControlEventEditingDidEnd]] subscribeNext:^(NSString *string) { if ([string length] > 0) { [channel.followingTerminal sendNext:[string substringFromIndex:([string length] - 1)]]; } }]; }]; (文本字段失去焦点),以便我们可以正确处理它。由于频道正在向我们传达最新的文本值,我们需要对其进行编辑,以便该字段包含输入的最后一个字符。我们将该值发回通道终端。

latestEditing

最后,我们需要确定何时应将焦点设置为下一个字段。我们映射switchToLatest信号的值。现在,我们返回一个等待返回键按下的信号(注意这次不同的事件,这样如果用户通过触摸事件聚焦到不同的字段,我们就不会这样做)。该信号返回下一个文本字段。我们再次调用RACSignal *next = [[latestEditing map:^id(RACTuple *tuple) { RACTupleUnpack(__unused RACChannel *channel, UITextField *field) = tuple; return [[[field rac_signalForControlEvents:UIControlEventEditingDidEndOnExit] take:1] map:^id(id value) { NSUInteger currentIndex = [fields indexOfObject:field]; NSUInteger nextIndex = currentIndex + 1; nextIndex %= fields.count; return fields[nextIndex]; }]; }] switchToLatest]; ,以便我们可以直接订阅最新的内部信号。

[next subscribeNext:^(UITextField *field) {
    [field becomeFirstResponder];
}];

最后,设置正确的字段成为第一个响应者。

fields

请注意,此解决方案可以容纳任意数量的文本字段 - 要更新代码,您需要做的就是以正确的顺序添加{{1}}数组的新字段。

我建议你看看ReactiveFormlets项目,看看如何使用ReactiveCocoa。