2013-05-01 8 views
5

ReactiveCocoaを使用してアプリケーションを構築しています。トップビューは、プルダウンしてからプッシュバックできるメニューです。私は2つの異なるジェスチャ認識器を使用しなければなりません - 一つは引き下げ、もう一つは押し戻すためです。一度に有効にできるのは1つだけです。問題があります。状態。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をたくさん持っていて、それはジャッキーと思われます。

  2. menuFinishedTransitionSubjectは、本質的にジェスチャ認識装置の状態を管理するために使用されます。私は彼らのenabledプロパティを結束させようとしました。ここにアドバイスはありますか?

答えて

5

明示的サブスクリプションは、一般的に命令コードの書き換えにはあまり意味がないためです。

最初に示したコードに基づいて、headerMovementSubjectdownwardHeaderPanSubject(およびその他の場所)からの値のみが入力されているようです。その代わりに、変換として記述するための簡単な候補だ:

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); 
    }]; 

その後、代わりに副作用としてself.headerViewController.view.frameを操作する、我々はバインディングを使用することができます。

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

私たちは、ブール値で同様のことを行うことができますmenuFinishedTransitionSubject中:

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

残念ながら、-flashScrollBarsはまだ副作用として呼び出される必要があるが、私たちはLEAですることができますSTは、ブロックの外にフィルタリングを持ち上げる:あなたは本当に空想を取得したい場合は

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

     [self.headerViewController flashScrollBars]; 
    }]; 

、ジェスチャー認識ロジックの多くは、代わりにストリーム変換で表現することができ、そしてアニメーションがReactiveCocoaLayoutを用いて実施することもできるが、それはですそれ自身の書き換え。

+0

'headerMovementSubject'がムーブアップジェスチャ認識器(および対応する信号)から供給されていた場合、それはどのように機能しますか?私はまだ明示的なサブジェクトプロパティが必要でしょうか? –

+0

主語は基本的に可変変数なので、コードの匂いがしばしばあります。代わりに、私はmove-downとmove-upジェスチャ認識子を(例えば '+ combineLatest:'を使って)上記の変換に繋がるものに組み合わせることに焦点を合わせます。 –

+0

Gotcha。私はそれがどのように機能するかを見ていると思う。ありがとう! –

関連する問題