2012-07-14 7 views
20

UIScrollViewとそのサブビューの両方でサブビュー内のすべてのタッチイベントを受け取るようにしたい。それぞれが独自の方法で対応することができます。UIScrollViewとそのサブビューの両方がタッチに応答するようにする

また、タップジェスチャーがサブビューに転送された場合は、すべて正常に動作します。

多くの人々がこの一般的な領域で苦労しています。ここでは多くの関連の質問のほんの一部です:

How does UIScrollView steal touches from its subviews
How to steal touches from UIScrollView?
How to Cancel Scrolling in UIScrollView

ちなみに、私はhitTestをオーバーライドする場合:withEvent:スクロールビューでは、私がいる限りuserInteractionEnabledがYESであるとタッチを参照しています。しかしそれは実際に私の問題を解決するものではありません:

1)その時点で、タップかどうかわかりません。
2)場合によっては、userInteractionEnabledをNOに設定する必要があります。

EDIT:明確にするために、はい、私はタップをパンとは別に扱いたいと思います。タップはサブビューで処理する必要があります。パンは通常の方法でスクロールビューで処理できます。

+4

NOにuserInteractionEnabledを設定し、ユーザインタラクションデータを期待パラドックスを粉砕宇宙のように思えます。 – borrrden

+0

タッチとスクロールを区別したいですか? – shannoga

答えて

38

まず、免責事項から延長されます。 UIScrollViewuserInteractionEnabledNOに設定すると、タッチイベントはサブビューに渡されません。私が知っている限り、例外は1つあります。UIScrollViewのスーパービューでタッチイベントを傍受し、具体的にはこれらのイベントをUIScrollViewのサブビューに渡します。正直言って、なぜあなたがこれをやりたいのか分からない。特定のUIScrollView機能を無効にしたい場合(スクロールなど)、UserInteractionを無効にしなくても簡単に行うことができます。

ご質問がありましたら、サブビューに渡されたUIScrollView およびで処理するタップイベントが必要ですか?どのような場合でも(ジェスチャーが何であれ)、私はあなたが探しているのはプロトコルUIGestureRecognizerDelegateのプロトコルメソッドgestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:です。あなたのサブビューでは、あなたが持っているジェスチャーレコグナイザがあれば、そのジェスチャレコグナイザのデリゲートを設定します(おそらく、どんなクラスでもUIGestureReconginzerを設定しています)。上記の方法をオーバーライドし、YESを返します。今、このジェスチャーは、ジェスチャー(あなたのケースでは、タップ)を「盗んだ」可能性のある他のレコグナイザーと一緒に認識されます。この方法を使用すると、特定の種類のジェスチャをサブビューに送信するか、特定の状況でのみジェスチャを送信するようにコードを微調整することさえできます。それはあなたに多くの制御を与えます。ただ、この方法、特にこの部分については必ずお読みください:gestureRecognizerまたはotherGestureRecognizer によってジェスチャーの認識のいずれかがそのジェスチャーを認識することから 他のジェスチャー認識をブロックするとき

このメソッドが呼び出されます。 YESを返す は、同時認識を保証することに注意してください。一方、他のジェスチャ認識装置の 代理人がYESを返す可能性があるので、 をNOに戻すと、 の同時認識を防ぐことが保証されません。

もちろん、注意点があります。これはジェスチャー認識ツールにのみ適用されます。したがって、タッチを処理するためにtouchesBegan:,touchesEndedなどを使用しようとすると、まだ問題が発生する可能性があります。もちろん、hitTest:を使って生のタッチイベントをサブビューに送ることはできますが、なぜですか? UIViewのメソッドを使用してイベントを処理する理由、UIGestureRecognizerをビューに添付してその機能をすべて無料で利用できるようになったのはなぜですか?標準UIGestureRecognizerが提供できない方法で処理する必要がある場合は、サブクラスUIGestureRecognizerを処理し、そこで処理します。そうすれば、あなた自身のカスタムタッチ処理と一緒にUIGestureRecognizerのすべての機能を得ることができます。デベロッパーがUIViewで使用しているカスタムタッチ処理コードのほとんど(すべてではないにしても)を、AppleがUIGestureRecognizerに置き換えようとしていたと思う。コードの再利用が可能で、どのコードがどのようなタッチイベントを処理するのかを緩和するのが簡単です。

+1

恐ろしく、ありがとう!他の誰かがこれを読んでいる場合は、UIScrollViewメソッドを使用して同様の効果を得ることができるかもしれません。touchesShouldCancelInContentView –

+0

「無効にすることなく簡単にUIScrollViewの特定の機能を無効にしたい場合(スクロールなど) UserInteraction " - >" myScrollView.scrollEnabled = false " – RGML

0

タッチとスクロールの違いが必要な場合は、タッチが移動したかどうかをテストできます。これがタップであれば、touchHasBeenMovedは呼び出されず、これはタッチであるとみなすことができます。

この時点で、ムービーが賞賛されているかどうかを示すブール値を設定し、このブール値を他のメソッドの条件として設定できます。

私は道路上にいますが、それが必要な場合は後で説明することができます。

3

これは助けになるかどうかは分かりませんが、同様の問題がありました。スクロールビューでダブルタップを処理したいが、シングルタップをサブビューに転送したいと思っていました。ここではもちろんCustomScrollView

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 

    UITouch* touch = [touches anyObject]; 
    // Coordinates 
    CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]]; 

    // One tap, forward 
    if(touch.tapCount == 1){ 
     // for each subview 
     for(UIView* overlayView in self.subviews){ 
      // Forward to my subclasss only 
      if([overlayView isKindOfClass:[OverlayView class]]){ 
       // translate coordinate 
       CGPoint newPoint = [touch locationInView:overlayView]; 
       //NSLog(@"%@",NSStringFromCGPoint(newPoint)); 

       BOOL isInside = [overlayView pointInside:newPoint withEvent:event]; 
       //if subview is hit 
       if(isInside){ 
        Forwarding 
        [overlayView touchesEnded:touches withEvent:event]; 
        break; 
       } 
      } 
     } 

    } 
    // double tap : handle zoom 
    else if(touch.tapCount == 2){ 

     if(self.zoomScale == self.maximumZoomScale){ 
      [self setZoomScale:[self minimumZoomScale] animated:YES]; 
     } else { 
      CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point];    

      [self zoomToRect:zoomRect animated:YES]; 
     } 

     [self setNeedsDisplay]; 

    } 
} 

で使用されるコードは、効果的なコードを変更する必要がありますが、この時点で、あなたはあなたがイベントを転送する必要があるかどうかを判断するために必要なすべての情報を持っている必要があります。 touchesMoved:withEvent:という別のメソッドでこれを実装する必要があります。

これは役に立ちます。

0

100%正確ではなく目的を達成するためのヒックな方法は、UIWindowをサブクラス化して - (void)sendEvent:(UIEvent *)イベントをオーバーライドすることです。

簡単な例:SecondResponderWindow.m

//SecondResponderWindow.m 

- (void)forwardTouchBegan:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchBegan:touch onView:aView]; 
} 
- (void)forwardTouchMoved:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchMoved:touch onView:aView]; 
} 
- (void)forwardTouchEnded:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchEnded:touch onView:aView]; 
} 

- (void)sendEvent:(UIEvent *)event { 
    [super sendEvent:event]; 

    if (viewToObserve == nil || controllerThatObserves == nil) return; 

    NSSet *touches = [event allTouches]; 
    UITouch *touch = [touches anyObject]; 
    if ([touch.view isDescendantOfView:viewToObserve] == NO) return; 

    CGPoint tapPoint = [touch locationInView:viewToObserve]; 
    NSValue *pointValue = [NSValue valueWithCGPoint:tapPoint]; 

    if (touch.phase == UITouchPhaseBegan) 
     [self forwardTouchBegan:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseMoved) 
     [self forwardTouchMoved:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseEnded) 
     [self forwardTouchEnded:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseCancelled) 
     [self forwardTouchEnded:pointValue onView:touch.view]; 
} 

でSecondResponderWindow.hヘッダー内

//SecondResponderWindow.h 

@protocol SecondResponderWindowDelegate 
- (void)userTouchBegan:(id)tapPoint onView:(UIView*)aView; 
- (void)userTouchMoved:(id)tapPoint onView:(UIView*)aView; 
- (void)userTouchEnded:(id)tapPoint onView:(UIView*)aView; 
@end 

@interface SecondResponderWindow : UIWindow 
@property (nonatomic, retain) UIView *viewToObserve; 
@property (nonatomic, assign) id <SecondResponderWindowDelegate> controllerThatObserves; 
@end 

それは100%あなたが期待していたものに準拠していないのです - あなたの第二のレスポンダので、 viewは-touchDidBegin:を介してネイティブにtouchイベントを処理しません。つまり、SecondResponderWindowDelegateを実装する必要があります。しかし、このハックは、追加のレスポンダでタッチイベントを処理することを可能にします。

このメソッドは、触発さとMITHIN KUMARのTapDetectingWindow

2

私はこの同じ問題を抱えていましたが、スクロールビューはUIPageViewControllerの中にあったため、少し違った扱いをしなければなりませんでした。

UIScrollViewの各認識器のcancelsTouchesInViewプロパティをfalseに変更すると、UIPageViewControllerのボタンに触れることができました。

私はviewDidLoadにこのコードを追加することによって、そのようにした:

guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else { 
    print("No gesture recognizers on scrollview.") 
    return 
} 

for recognizer in recognizers { 
    recognizer.cancelsTouchesInView = false 
} 
関連する問題