2013-05-01 2 views
5

ReactiveCocoa를 사용하여 응용 프로그램을 작성하고 있습니다. 윗면보기는 아래로 끌어 내려서 되돌릴 수있는 메뉴입니다. 나는 두개의 다른 몸짓 인식기를 사용해야한다. 하나는 잡아 당기기위한 것이고 다른 하나는 뒤로 밀기위한 것이다. 한 번에 하나만 활성화 할 수 있으며 문제가 있습니다. 상태.제스처 인식 자와 함께 ReactiveCocoa를 사용하는 방법

제스처 인식기를 설정하기 위해 BlocksKit 확장 프로그램을 사용하고 있습니다. 내 initWithNibName:bundle: 방법에서

self.panHeaderDownGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) { 
    UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)sender; 

    CGPoint translation = [recognizer translationInView:self.view]; 

    if (state == UIGestureRecognizerStateChanged) 
    { 
     [self.downwardHeaderPanSubject sendNext:@(translation.y)]; 
    } 
    else if (state == UIGestureRecognizerStateEnded) 
    { 
     // Determine the direction the finger is moving and ensure if it was moving down, that it exceeds the minimum threshold for opening the menu. 
     BOOL movingDown = ([recognizer velocityInView:self.view].y > 0 && translation.y > kMoveDownThreshold); 

     // Animate the change 
     [UIView animateWithDuration:0.25f animations:^{ 
      if (movingDown) 
      { 
       [self.downwardHeaderPanSubject sendNext:@(kMaximumHeaderTranslationThreshold)]; 
      } 
      else 
      { 
       [self.downwardHeaderPanSubject sendNext:@(0)]; 
      } 
     } completion:^(BOOL finished) { 
      [self.menuFinishedTransitionSubject sendNext:@(movingDown)]; 
     }]; 
    } 
}]; 

, 나는 다음과 같은 RACSubject의를 설정하고있다.

self.headerMovementSubject = [RACSubject subject]; 
[self.headerMovementSubject subscribeNext:^(id x) { 
    @strongify(self); 

    // This is the ratio of the movement. 0 is closed and 1 is open. 
    // Values less than zero are treated as zero. 
    // Values greater than one are valid and will be extrapolated beyond the fully open menu. 
    CGFloat ratio = [x floatValue]; 

    CGRect headerFrame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), kHeaderHeight + ratio * kMaximumHeaderTranslationThreshold); 

    if (ratio < 0) 
    {    
     headerFrame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), kHeaderHeight); 
    } 

    self.headerViewController.view.frame = headerFrame; 
}]; 

// This subject is responsible for receiving translations from a gesture recognizers and turning 
// thos values into ratios. These ratios are fead into other signals. 
self.downwardHeaderPanSubject = [RACSubject subject]; 
[self.downwardHeaderPanSubject subscribeNext:^(NSNumber *translation) { 
    @strongify(self); 
    CGFloat verticalTranslation = [translation floatValue]; 

    CGFloat effectiveRatio = 0.0f; 

    if (verticalTranslation <= 0) 
    { 
     effectiveRatio = 0.0f; 
    } 
    else if (verticalTranslation <= kMaximumHeaderTranslationThreshold) 
    { 
     effectiveRatio = fabsf(verticalTranslation/kMaximumHeaderTranslationThreshold); 
    } 
    else 
    { 
     CGFloat overshoot = verticalTranslation - kMaximumHeaderTranslationThreshold; 
     CGFloat y = 2 * sqrtf(overshoot + 1) - 2; 
     effectiveRatio = 1.0f + (y/kMaximumHeaderTranslationThreshold); 
    } 

    [self.headerMovementSubject sendNext:@(effectiveRatio)]; 
}]; 

// This subject is responsible for mapping this value to other signals and state (ugh). 
self.menuFinishedTransitionSubject = [RACReplaySubject subject]; 
[self.menuFinishedTransitionSubject subscribeNext:^(NSNumber *menuIsOpenNumber) { 
    @strongify(self); 

    BOOL menuIsOpen = menuIsOpenNumber.boolValue; 

    self.panHeaderDownGestureRecognizer.enabled = !menuIsOpen; 
    self.panHeaderUpGestureRecognizer.enabled = menuIsOpen; 
    self.otherViewController.view.userInteractionEnabled = !menuIsOpen; 

    if (menuIsOpen) 
    { 
     [self.headerViewController flashScrollBars]; 
    } 
}]; 

여기에 많은 것이 있습니다. 문제는 내가 여기에 나열한 피사체의 수 (팬 제스처 인식기의 피사체의 수)도 거의 두 배로 늘었 기 때문에 악화됩니다. 더하기 푸터와 유사한 상호 작용을위한 또 다른 인식기 세트 . 그것은 많은 과목입니다.

내 질문은 두 부분입니다 : 내가 원하는 체인의 종류를 설정하는 더 좋은 방법은

  1. 있습니까? 나는 푸시 업 몸짓에서 피험자 중 일부를 재사용하고 있습니다. 정말 비슷합니다. 나는 RACSubjects을 많이 가지고 있고 그것은 janky처럼 보인다.
  2. menuFinishedTransitionSubject은 본질적으로 제스처 인식기의 상태를 관리하는 데 사용됩니다. 행운을 빌려서 enabled 속성을 바인딩하려고했습니다. 여기에 어떤 조언이 있니?

답변

5

일반적으로 명시 적 구독은 명령형 코드를 다시 작성하기위한 결실로 가득차 있기 때문에 집중적으로 살펴 보겠습니다.

먼저 표시된 코드에 따르면 headerMovementSubjectdownwardHeaderPanSubject (및 다른 곳)의 값만 제공됩니다. 그 대신 변환으로 작성하기위한 쉬운 후보의 :

RAC(self.headerViewController.view.frame) = headerFrameSignal; 

우리는 논리 값과 비슷한 일을 수행 할 수 있습니다 대신 부작용으로 self.headerViewController.view.frame를 조작, 그리고

RACSignal *headerFrameSignal = [[self.downwardHeaderPanSubject 
    map:^(NSNumber *translation) { 
     CGFloat verticalTranslation = [translation floatValue]; 
     CGFloat effectiveRatio = 0.0f; 

     // Calculate effectiveRatio. 

     return @(effectiveRatio); 
    }] 
    map:^(NSNumber *effectiveRatio) { 
     // Calculate headerFrame. 

     return @(headerFrame); 
    }]; 

, 우리는 바인딩을 사용할 수 있습니다 menuFinishedTransitionSubject에서 것은 :

RAC(self.panHeaderDownGestureRecognizer.enabled) = [self.menuFinishedTransitionSubject not]; 
RAC(self.panHeaderUpGestureRecognizer.enabled) = self.menuFinishedTransitionSubject; 
RAC(self.otherViewController.view.userInteractionEnabled) = [self.menuFinishedTransitionSubject not]; 

불행하게도, -flashScrollBars은 여전히 ​​부작용으로 호출 할 필요가있다, 그러나 우리는 레아 분에서 수 당신은 정말 멋진 얻고 싶은 경우에

[[self.menuFinishedTransitionSubject 
    filter:^(NSNumber *menuIsOpen) { 
     return menuIsOpen.boolValue; 
    }] 
    subscribeNext:^(id _) { 
     @strongify(self); 

     [self.headerViewController flashScrollBars]; 
    }]; 

, 제스처 인식기 로직의 많은 대신 스트림 변환으로 표현 될 수 있으며, 애니메이션 ReactiveCocoaLayout로 구현 될 수 있지만, 그건 : 성 블록에서 필터링을 들어 올려 그 자신의 재 작성.

+0

'headerMovementSubject'가 제스처 인식기 (및 해당 신호)에서 제공되는 경우 어떻게 작동합니까? 나는 명백한 주제 재산이 여전히 필요합니까? –

+0

피험자는 기본적으로 변경 가능한 변수이므로 코드 냄새가 자주 발생합니다. 대신, 위와 같은 변환에 사용할 수있는 무언가에'+ combineLatest :'를 사용하여 제스처 인식기 주제를 결합하는 데 중점을 둡니다. –

+0

Gotcha.그게 어떻게 작동하는지 알 것 같습니다. 감사! –