Реактивное какао, расщепление сигнала без дублирования кода?

Я пытаюсь изменить метку на кнопке на вызываемом селекторе.

Похоже, что код дублируется. Есть ли способ, может быть, для меня сейчас неочевидно переключать сигнал после карты? или нет?

[[[pressedStart map:^id(id value) {
    UIButton* button = value;
    BOOL transform = [button.titleLabel.text isEqualToString:@"Start"];
    return [NSNumber numberWithBool:transform];

}] filter:^BOOL(id value) {
    return [value boolValue];
}] subscribeNext:^(id x) {
    self.start.titleLabel.text = @"Stop";
}];

[[[pressedStart map:^id(id value) {
    UIButton* button = value;
    BOOL transform = [button.titleLabel.text isEqualToString:@"Stop"];
    return [NSNumber numberWithBool:transform];
}] filter:^BOOL(id value) {
    return [value boolValue];
}] subscribeNext:^(id x) {
   self.start.titleLabel.text = @"Start";
}];

1 ответ

Решение

Прежде всего, чтобы изменить название кнопки, вы должны вызвать ее setTitle:forState: метод.

Также обратите внимание, что с помощью self внутри subscribeNext блок может создать цикл сохранения (и, следовательно, утечку памяти). Вы можете прочитать больше об этом в этом ответе. Ты можешь использовать @weakify / @strongify макросы или, как уже упоминалось в этом ответе, использовать rac_liftSelectors:withSignals: метод (который имхо кажется чище).

Ваш код может быть упрощен, так как вам вообще не нужно разделять сигнал. Вы можете использовать простое условие внутри map заблокировать и вернуть значение, которое должно быть названием кнопки после ее нажатия. Это значение будет отправлено как next значение результирующего сигнала. Вы также можете использовать startWith: Оператор, чтобы установить начальное значение (я думаю, это должно быть "Пуск").

RACSignal *buttonTextSignal = [[pressedStart map:^id(UIButton *buttonPressed) {
    return [buttonPressed.titleLabel.text isEqualToString:@"Start"] ? @"Stop" : @"Start";
}]
startWith:@"Start"];
[self.start rac_liftSelector:@selector(setTitle:forState:) withSignals:buttonTextSignal, [RACSignal return:@(UIControlStateNormal)], nil];

Что значит rac_liftSelector:withSignals: делать? Каждый раз один из signals отправляет его next значение, он вызывает метод, определенный selector (в этом случае setTitle:forState:). Метод вызывается с next значения signals как его параметры. Так что в нашем случае это будет первоначально вызвать:

[self.startButton setTitle:@"Start" forState:UIControlStateNormal];

Если вы хотите установить одно свойство (скажем, titleLabel.text), вы можете связать это с RAC макрос:

RAC(self.startButton, titleLabel.text) = buttonTextSignal;

К сожалению, это работает только для установки свойств, и в вашем случае вам нужно вызвать метод с двумя аргументами, поэтому вы должны использовать rac_liftSelector:withSignals,


Как я уже сказал, вы можете достичь желаемого результата, используя subscribeNext:

@weakify(self);
RACSignal *buttonTextSignal = [[[pressedStart map:^id(UIButton *buttonPressed) {
    return [buttonPressed.titleLabel.text isEqualToString:@"Start"] ? @"Stop" : @"Start";
}]
startWith:@"Start"]
subscribeNext:^(NSString *title) {
    @strongify(self);
    [self.startButton setTitle:title forState:UIControlStateNormal];
}];

Но, как вы видите, вы должны проявлять особую осторожность, чтобы избежать цикла сохранения, используя @weakify а также @strongify макросы.

Другие вопросы по тегам