2014-01-09 6 views
4

В моем приложении у меня есть сигнал, который вызывает асинхронную сетевую активность через flattenMap. Я хочу показать индикатор загрузки во время выполнения сетевой активности.ReactiveCocoa - Изменение побочных эффектов в сигналы

Мое текущее решение работает просто отлично:

[[[[self.signInButton 
    rac_signalForControlEvents:UIControlEventTouchUpInside] 
    doNext:^(id x) { 
     // show the loading indicator as a side-effect 
     self.loadingIndicator.hidden = NO; 
    }] 
    flattenMap:^id(id x) { 
     return [self doSomethingAsync]; 
    }] 
    subscribeNext:^(NSNumber *result) { 
     // hide the indicator again 
     self.loadingIndicator.hidden = YES; 
     // do something with the results 
    }]; 

Это работает, но я хотел бы изменить код выше, так что hidden свойство индикатора загрузки может быть установлен с помощью сигнала.

Возможно ли это?

В другом месте моего приложения у меня есть более сложные требования, когда видимость элемента зависит от нескольких разных «событий», возможность их компоновки через сигналы будет намного лучше.

ответ

13

RACCommand является специально построен именно для этого случая использования, и, как правило, приводит к значительно более простой код, чем альтернативы:

@weakify(self); 

RACCommand *signInCommand = [[RACCommand alloc] initWithSignalBlock:^(id _) { 
    @strongify(self); 
    return [self doSomethingAsync]; 
}]; 

self.signInButton.rac_command = signInCommand; 

// Show the loading indicator while signing in. 
RAC(self.loadingIndicator, hidden) = [signInCommand.executing not]; 
2

Похоже, что ваш сигнал: когда signInButtonSignal или resultSignal отправьте значение, инвертируйте последнее значение hidden. Это достаточно легко.

[[[hiddenSig replayLast] not] sample:[RACSignal merge:@[signInButtonSignal, resultSignal]]; 

Я использую именно эту конструкцию для ситуации, аналогичной вашей. Это может быть хорошо, чтобы обернуть его в оператор:

- (RACSignal *)toggle:(RACSignal *)toggler 
{ 
    return [[[self replayLast] not] sample:toggler]; 
} 

Тогда у вас есть только

[hiddenSig toggle:[RACSignal merge:@[signInButtonSignal, resultSignal]]]; 

Другая возможность может быть метод класса, связывая состояние до отображения блока:

+ (RACSignal *)toggle:(RACSignal *)toggler initially:(BOOL)initial 
{ 
    __block BOOL currVal = initial; 
    return [[toggler map:^id (id _) { 
     currVal = !currVal; 
     return @(currVal); 
    }] startWith:@(initial)]; 
} 

, а затем

[RACSignal toggle:[RACSignal merge:@[signInButtonSignal, resultSignal]] 
     initially:NO]; 
+0

+1 Спасибо за это - я попытаюсь обернуть голову вокруг вашего ответа и в ближайшее время попробовать. Очень признателен. – ColinE

+0

Вы ставите! Я рад, что вы воскресили этот вопрос; Я видел это на днях, но вы удалили его, прежде чем я смог вернуться к ответу. –

+0

Я удалил его, потому что я * думал * Я нашел ответ, см. Эту проблему в github https://github.com/ReactiveCocoa/ReactiveCocoa/issues/469, однако с RC2.0 некоторые из методов, описанных в ответе, больше недоступно. Разрыв широко распространенных изменений от 1.0-2.0 может сделать его немного сложным изучением RC ... и с RC3.0 они планируют продолжить тренд с некоторыми дальнейшими крупномасштабными изменениями разрыва. Во всяком случае - это отличная инфраструктура. – ColinE

2

answer from Josh помог немного, но в итоге я нашел более простое решение. Просто разбивая трубопровод на два сигнала, один для нажатия кнопки, другой для последующей асинхронной активности. Затем я объединил два, чтобы дать сигнал, который я использовал для привязки к loadingIndicator «s hidden собственности:

// a signal that triggers sign-in 
RACSignal *signInStartSignal = [self.signInButton 
         rac_signalForControlEvents:UIControlEventTouchUpInside]; 

// a signal that emits the sign in result 
RACSignal *signInResultSignal = 
    [signInStartSignal 
    flattenMap:^id(id x) { 
     return [self doSomethingAsync]; 
    }]; 

[signInResultSignal 
    subscribeNext:^(NSNumber *result) { 
    // do something based on the result 
    }]; 

// merge the two signals 
RACSignal *signInInProgress = 
    [[RACSignal merge:@[signInResultSignal, signInStartSignal]] 
    map:^id(id value) { 
     // if the signal value is a UIButton, the signal that 
     // just fired was the signInStartSignal 
     return @(![[value class] isSubclassOfClass:[UIButton class]]); 
    }]; 

RAC(self.signInFailureText,hidden) = signInInProgress; 
+0

Слияние, но различение двух сигналов и отображение значений в буле! Решение _Nice_. Очень хорошо. –

+0

Спасибо, рад, что вам понравилось :-) – ColinE

Смежные вопросы