2015-06-27 72 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)。
  • 爲了保證純度並擺脫@weakify指令,textChangeSignal現在使用方便的scanWithStart:reduce:方法,該方法允許您訪問累加器(對於使用遞增或遞減數字的方法來說,這非常適用)。
  • start的文本現在正在被rac_liftSelector函數改變,該函數需要RACSignals並在所有人都解僱時解開它們。
  • 您的flattenMap:pressedStart替換爲[self startTimer]現在使用scanWithStart:reduce,這是一個更加有效的方法來保持計數。

我不知道,如果你是通過使測試updateTable包含完成信號,但它肯定會導致邏輯問題與你的pressedButtonflattenMap:,得到的反饋迴路,最終導致程序崩潰時的堆棧溢出。

+1

掃描時有大理石圖嗎?我不太確定它是如何工作的。 –

+0

http://rxmarbles.com/#scan,但我也會嘗試解釋一下。 Scan有能力保留狀態,這是'running'變量的意思,它是一個有狀態的參數,它被傳入每個塊,並被該塊返回的內容替換。這對於可以在兩個不同輸出之間進行觸發的信號非常有用,例如@「Stop」和@「Start」,基於前一個輸出。 –

+0

所以我猜想在開始掃描之前首先應用@ 0信號,然後處理下一個信號? –

相關問題