web-dev-qa-db-ja.com

同じ方向にスクロールするネストされたUIScrollViewのタッチの処理

2つのネストされたUIScrollViewがあり、どちらも垂直方向にスクロールします。内側のスクロールビューをスクロールさせる前に、最初に外側のスクロールビューを最大範囲までスクロールする必要があります。内側のスクロールビューは、外側のスクロールビューが最大範囲に達するまでスクロールできないようにする必要があります。これがイラストです: Nested Scrollviews Diagram

左の図では、Scrollview B内の垂直ドラッグでScrollview Aが移動し、Scrollview Bはスクロールできません(ただし、タッチ/タップを受信できる必要があります)。 Scrollview Aが最大範囲に達すると(Scrollview Bが画面の上部に到達すると)、Scrollview Bがスクロールするはずです。これは、1つの連続した動きで機能する必要があります。

ScrollView BscrollEnabledScrollView AscrollViewDidScroll:デリゲートメソッドから切り替えようとしましたが、1つでは機能しないため、これは実行可能な解決策ではないようです。連続動作(例:Scrollview Bが画面の上部に到達した後、ユーザーは離してもう一度タッチする必要があります)。

1つの連続した動きで動作するようにこれを実装するための最良の方法は何ですか?

27
user2393462435

私の場合、外側のScrollViewのサブクラス化UIScrollViewを解決しました。

class MYOuterScrollView: UIScrollView, UIGestureRecognizerDelegate {

override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {

    return true
}

}
4
Davide Candita

私は次の方法で問題を解決しました。複雑すぎるように見えるので、あまり満足していませんが、機能します(以下のコードは、UIがより複雑であるため、コードの簡略化されたテストされていないバージョンであることに注意してください)。

スクロールを制御する3つのプロパティがあります。

@property (nonatomic, assign) CGFloat currentPanY;
@property (nonatomic, assign) BOOL    scrollA;
@property (nonatomic, assign) BOOL    scrollB;

2段階スクロール:

Bのスクロールを無効にし、Aのスクロールを有効にします。
これにより、Aをスクロールできます。

Aが最大位置に達したら、Aのスクロールを無効にし、Bのスクロールを有効にします。

-(void)scrollViewDidScroll: (UIScrollView *)scrollView {
    if (scrollView.contentOffset.y >= self.maxScrollUpOffset) {
        [scrollView setContentOffset:CGPointMake(0, self.maxScrollUpOffset) animated:NO];        
        self.scrollviewA.scrollEnabled = NO;
        self.scrollviewB.scrollEnabled = YES;
        self.scrollB = YES;
    }
}

これにより、次の効果が得られます。
Aを上にスクロールすると、最大サイズに達するとスクロールが停止します。ただし、AのパンジェスチャレコグナイザーはそのアクションをBのパンジェスチャレコグナイザーに転送しないため、Bはスクロールを開始しません。したがって、指を離して2番目のスクロールを開始する必要があります。次に、Bがスクロールします。これにより、2段階のスクロールが可能になります。

連続スクロール:

連続スクロールの場合、Aのスクロールを開始した指が上に移動し続けている間、Bはスクロールする必要があります。これを検出するために、Aにパンジェスチャレコグナイザーを追加し、AとBの組み込みジェスチャレコグナイザーと同時にジェスチャを検出できるようにしました。

 - (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

この追加のパンジェスチャレコグナイザーのアクションでは、Aのスクロール制限に達した後に指が上に移動した距離を計算します。この距離だけ、Bはプログラムでスクロールされます。

- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)recognizer {
    if (recognizer.state != UIGestureRecognizerStateChanged) {
        self.currentPanY = 0;
        self.scrollB = NO;
        self.scrollA = NO;
    } else {
        CGPoint currentTranslation = [recognizer translationInView:self.scrollviewA];
        CGFloat currentYup = currentTranslation.y;

        if (self.scrollA || self.scrollB) {
            if (self.currentPanY == 0) {
                self.currentPanY = currentYup;
            }

            CGFloat additionalYup = self.currentPanY - currentYup;
            if (self.scrollA) {
                CGFloat offset = self.scrollviewA.scrollUpOffset + additionalYup;
                if (offset >= 0) {
                    self.scrollviewA.contentOffset = CGPointMake(0, offset);
                } else {
                    self.scrollviewA.contentOffset = CGPointZero;
                }
            } else if (self.scrollB){
                self.scrollviewB.contentOffset = CGPointMake(0, additionalYup);
            }
        }
    }
}  

まだ欠点があります:
スクロールを開始し、指を離して、scrollViewを減速させると、追加のパンジェスチャ認識機能がパンジェスチャを認識しないため、2段階のスクロールのように動作します。

4

これが1つの連続した動きで機能する必要があるという要件は、答えを示しています。2つではなく1つのUIScrollViewを使用する必要があります。

スクロールビューが1つしかない場合は、スクロールビューのlayoutSubviewsメソッドをオーバーライドし、そのコンテンツを再調整して、現在の値contentOffsetに基づいて視差効果を実行することで魔法を実行できます。 contentSizeが常に高さ全体を反映していることを確認してください(必要に応じて、layoutSubviews内のcontentSizeを更新することもできます)。

アーキテクチャの場合は、既存の図を使用して、ScrollviewBをViewBに置き換えるだけです。

3
Clafou

スクロールビューAのジェスチャレコグナイザーをスクロールビューBのジェスチャレコグナイザーに渡す必要があります。これは、不可能であると確信しています。代わりに、2つのスクロールビューのコンテンツを組み合わせて、1つの連続したモーションを作成してみませんか。このコードは、scrollViewAとBのコンテンツをAだけに結合します。

UIScrollView* scrollViewA = ...;
UIScrollView* scrollViewB = ...;
NSArray* subviews = scrollViewB.subviews;
for (int i = 0; i < subviews.count; i++)
{
    UIView* subview = [subviews objectAtIndex:i];
    CGRect frame = subview.frame;
    frame.Origin.y += scrollViewA.contentSize.height;
    subview.frame = frame;
    [scrollViewA addSubview:subview];
}
CGSize size = scrollViewA.contentSize;
size.height += scrollViewB.contentSize.height;
scrollViewA.contentSize = size;
1
Stephen Johnson