2015-06-27 2 views
1

Итак, я успешно включил кнопку включения и выключения, которая меняет ярлык.ReactiveCocoa takeUntil 2 возможных сигнала?

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

В любом случае мне нужно, чтобы закрыть тайм-процесс. Мне было интересно, есть ли способ остановить его, не используя одноразовый. Со вторым сигналом takeUntil.

Редактировать Я думаю, что то, что я пытался сделать, было немного обманчиво, позвольте мне показать мое текущее решение, которое работает.

-(RACSignal*) startTimer { 
    return [[RACSignal interval:1.0 
      onScheduler:[RACScheduler mainThreadScheduler]] 
      startWith:[NSDate date]]; 
} 

-(void) viewWillAppear:(BOOL)animated {} 

-(void) viewDidLoad { 

    self.tableView.delegate = self; 
    self.tableView.dataSource = self; 

    RACSignal* pressedStart = [self.start rac_signalForControlEvents:UIControlEventTouchUpInside]; 

    @weakify(self); 
    RACSignal* textChangeSignal = [pressedStart map:^id(id value) { 
     @strongify(self); 
     return [self.start.titleLabel.text isEqualToString:@"Start"] ? @"Stop" : @"Start"; 
    }]; 

    // Changes the title 
    [textChangeSignal subscribeNext:^(NSString* text) { 
     @strongify(self); 
     [self.start setTitle:text forState:UIControlStateNormal]; 
    }]; 

    RACSignal* switchSignal = [[textChangeSignal map:^id(NSString* string) { 
     return [string isEqualToString:@"Stop"] ? @0 : @1; 

    }] filter:^BOOL(id value) { 
     NSLog(@"Switch %@",value); 
     return [value boolValue]; 
    }]; 


    [[self rac_signalForSelector:@selector(viewWillAppear:)] 
    subscribeNext:^(id x) { 


    }]; 

    static NSInteger t = 0; 
    // Remake's it self once it is on finished. 
    self.disposable = [[[textChangeSignal filter:^BOOL(NSString* text) { 
     return [text isEqualToString:@"Stop"] ? [@1 boolValue] : [@0 boolValue]; 
    }] 
            flattenMap:^RACStream *(id value) { 
             NSLog(@"Made new Sheduler"); 
             @strongify(self); 
             return [[self startTimer] takeUntil:switchSignal]; 
            }] subscribeNext:^(id x) { 
             NSLog(@"%@",x); 
             @strongify(self); 
             t = t + 1; 
             NSLog(@"%zd",t); 

             [self updateTable]; 
            }]; 

    [[self rac_signalForSelector:@selector(viewWillDisappear:)] subscribeNext:^(id x) { 
     NSLog(@"viewWillAppear Dispose"); 
     [self.disposable dispose]; 
    }]; 

} 


-(BOOL) isGroupedExcercisesLeft { 
    BOOL isGroupedLeft = NO; 
    for (int i =0;i < [self.excercises count]; i++) { 
     Excercise* ex = [self.excercises objectAtIndex:i]; 
     if(ex.complete == NO && ex.grouped == YES) { 
      isGroupedLeft = YES; 
      break; 
     } 
    } 
    return isGroupedLeft; 
} 

-(void) updateTable { 

    // Find the 
    NSInteger nextRow; 

    if (([self.excercises count] > 0 || self.excercises !=nil) && [self isGroupedExcercisesLeft]) { 

     for (int i =0;i < [self.excercises count]; i++) { 

      Excercise* ex = [self.excercises objectAtIndex:i]; 
      if(ex.complete == NO && ex.grouped == YES) { 
       nextRow = i; 
       break; 
      } 

     } 

     NSIndexPath* path = [NSIndexPath indexPathForItem:nextRow inSection:0]; 
     NSArray* indexPath = @[path]; 
     // update // 

     Excercise* ex = [self.excercises objectAtIndex:nextRow]; 
     [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES]; 
     if (ex.seconds <= 0) { 
      RLMRealm* db = [RLMRealm defaultRealm]; 
      [db beginWriteTransaction]; 
      ex.complete = YES; 
      [db commitWriteTransaction]; 
     } 
     else { 
      // Update Seconds 
      RLMRealm* db = [RLMRealm defaultRealm]; 
      [db beginWriteTransaction]; 
      ex.seconds = ex.seconds - 1000; 
      NSLog(@"Seconds: %zd",ex.seconds); 
      [db commitWriteTransaction]; 
      // Update table 
      [self.tableView reloadRowsAtIndexPaths:indexPath withRowAnimation:UITableViewRowAnimationNone]; 
     } 


    } else { 
     NSLog(@"Done"); 

     SIAlertView *alertView = [[SIAlertView alloc] initWithTitle:@"Deskercise" andMessage:@"Excercises Complete"]; 
     [alertView addButtonWithTitle:@"Ok" 
           type:SIAlertViewButtonTypeDefault 
           handler:^(SIAlertView *alert) { 

           }]; 

     alertView.transitionStyle = SIAlertViewTransitionStyleBounce; 
     [alertView show]; 
     NSLog(@"Dispose"); 
     [self.disposable dispose]; 

    } 

} 

ответ

1

Проблема с использованием takeUntil:self.completeSignal является то, что при изменении completeSignal на другое значение, оно не передается какой-либо функции, которая уже ждет переменной, которая completeSignal ранее державшего.

- (RACSignal*) startTimer { 
    @weakify(self) 
    return [[[RACSignal interval:1.0 
       onScheduler:[RACScheduler mainThreadScheduler]] 
     startWith:[NSDate date]] 
     takeUntil:[[self.start rac_signalForControlEvents:UIControlEventTouchUpInside] 
        merge:[[RACObserve(self, completeSignal) skip:1] flattenMap: 
          ^RACStream *(RACSignal * signal) { 
           @strongify(self) 
           return self.completeSignal; 
          }]] 
     ]; 
} 

Сигнал теперь наблюдения и уплощение completeSignal, который даст желаемый эффект. Сигналы, которые завершаются без отправки следующих событий, игнорируются takeUntil:, поэтому используйте self.completedSignal = [RACSignal return:nil], который отправляет одно следующее событие, а затем завершает.

Однако этот код не идеален, давайте посмотрим на лучшее решение.

@property (nonatomic, readwrite) RACSubject * completeSignal;

- (RACSignal*) startTimer { 
    return [[[RACSignal interval:1.0 
       onScheduler:[RACScheduler mainThreadScheduler]] 
     startWith:[NSDate date]] 
     takeUntil:[[self.start rac_signalForControlEvents:UIControlEventTouchUpInside] 
        merge:self.completeSignal] 
     ]; 
} 
- (void) viewDidLoad { 
    [super viewDidLoad]; 
    self.completeSignal = [RACSubject subject]; 
    self.tableView.delegate = self; 
    self.tableView.dataSource = self; 

    RACSignal * pressedStart = [self.start rac_signalForControlEvents:UIControlEventTouchUpInside]; 

    @weakify(self); 
    RACSignal* textChangeSignal = [[pressedStart startWith:nil] scanWithStart:@"Stop" reduce:^id(id running, id next) { 
     return @{@"Start":@"Stop", @"Stop":@"Start"}[running]; 
    }]; 

    [self.start 
    rac_liftSelector:@selector(setTitle:forState:) 
    withSignals:textChangeSignal, [RACSignal return:@(UIControlStateNormal)], nil]; 

    [[[pressedStart flattenMap:^RACStream *(id value) { //Using take:1 so that it doesn't get into a feedback loop 
     @strongify(self); 
     return [self startTimer]; 
    }] scanWithStart:@0 reduce:^id(NSNumber * running, NSNumber * next) { 
     return @(running.unsignedIntegerValue + 1); 
    }] subscribeNext:^(id x) { 
     @strongify(self); 
     [self updateTable]; 
     NSLog(@"%@", x); 
    }]; 
} 

- (void) updateTable { 
    //If you uncomment these then it'll cause a feedback loop for the signal that calls updateTable 

    //[self.start sendActionsForControlEvents:UIControlEventTouchUpInside]; 
    //[self.completeSignal sendNext:nil]; 
    if ([self.excercises count] > 0 || self.excercises !=nil) { 
    } else { 
    } 
} 

Давайте пройдемся по списку изменений:

  • completeSignal теперь RACSubject (с ручным управлением RACSignal).
  • Для чистоты и для устранения директивы @weakifytextChangeSignal теперь использует удобный метод scanWithStart:reduce:, который позволяет вам получить доступ к аккумулятору (это хорошо работает для методов, которые работают с увеличивающимся или уменьшающимся числом).
  • Текст start теперь изменяется функцией rac_liftSelector, которая принимает RACSignals и разворачивает их, когда все уволили.
  • Ваш flattenMap: для замены pressedStart с [self startTimer] теперь использует scanWithStart:reduce, что является гораздо более функциональным способом учета.

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

+1

Есть ли мраморная диаграмма при сканировании? Я не совсем уверен, как это работает. –

+0

http://rxmarbles.com/#scan, но я также попробую и объясню немного. Scan имеет возможность сохранять состояние, которое является переменной 'running', аргументом состояния, который передается для каждого блока и заменяется тем, что возвращается этим блоком. Это делает его очень полезным для сигнала, который может переключаться между двумя различными выходами, например, «Stop» и «Start», на основе предыдущего выхода. –

+0

Итак, я предполагаю, что сканирование с началом применяет сигнал @ 0 прежде, чем он обработает следующий сигнал? –

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