5

ReactiveCocoa を使用してアプリケーションを構築しています。トップビューは、プルダウンしてからプッシュアップできるメニューです。2 つの異なるジェスチャ認識機能を使用する必要があります。1 つは引き下げるため、もう 1 つは押し戻すためです。一度に有効にできるのは 1 つだけです。そこに私の問題があります。州。

BlocksKit 拡張機能を使用してジェスチャ認識エンジンをセットアップしています。

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

私のinitWithNibName:bundle:方法では、次の s を設定しています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];
    }
}];

ここでは多くのことが起こっています。この問題は、ここに挙げたサブジェクトの数のほぼ 2 倍 (パンアップ ジェスチャ レコグナイザーのサブジェクトも含む)と、フッターとの同様の相互作用のためのレコグナイザーの別のセットを持っているという事実によって悪化します。それは多くの科目です。

私の質問は2つの部分に分かれています:

  1. 私が望む種類の連鎖を設定するためのより良い方法はありますか? 腕立て伏せのジェスチャーでもいくつかの被写体を再利用していますが、これは非常によく似ています。私はたくさん持っていて、RACSubjectsジャンキーに見えます。
  2. menuFinishedTransitionSubject基本的に、ジェスチャ レコグナイザの状態を管理するために使用されます。私は彼らの財産を束縛しようとしましたenabledが、運がありませんでした。ここで何かアドバイスはありますか?
4

1 に答える 1

5

明示的なサブスクリプションに焦点を当てましょう。これは一般に、命令型コードを書き直すための簡単な成果です。

まず第一に、示されているコードに基づいて、headerMovementSubject値が供給されているだけのように見えますdownwardHeaderPanSubject(他のどこにも供給されていません)。これは、代わりに変換として書くための簡単な候補です:

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副作用として呼び出す必要がありますが、少なくともブロックからフィルタリングを取り除くことができます。

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

        [self.headerViewController flashScrollBars];
    }];

より凝ったものにしたい場合は、ジェスチャ認識ロジックの多くを代わりにストリーム変換で表すことができ、アニメーションはReactiveCocoaLayoutで実装できますが、それは独自の書き直しです。

于 2013-05-01T18:20:39.530 に答える