UIScrollViewとそのサブビューの両方が、サブビュー内のすべてのタッチイベントを受け取るようにします。それぞれが独自の方法で応答できます。
または、タップジェスチャがサブビューに転送された場合、すべてうまくいきます。
多くの人々がこの一般的な分野で苦労しています。関連する多くの質問のいくつかを次に示します。
IScrollViewはどのようにサブビューからタッチを盗みますか
IScrollViewからタッチを盗む方法
IScrollViewでスクロールをキャンセルする方法
ちなみに、スクロールビューでhitTest:withEvent:をオーバーライドすると、userInteractionEnabledがYESである限りタッチが表示されます。しかし、それは私の問題を実際に解決しません:
1)その時点では、タップかどうかはわかりません。
2)時々、userInteractionEnabledをNOに設定する必要があります。
編集:はい、明確にするために、私はタップを鍋とは別に扱いたいです。タップはサブビューで処理する必要があります。パンは、通常の方法でスクロールビューで処理できます。
まず、免責事項。 userInteractionEnabled
でNO
をUIScrollView
に設定すると、タッチイベントはサブビューに渡されません。私の知る限り、例外は1つあります。例外はUIScrollView
のスーパービューでタッチイベントをインターセプトし、特にこれらのイベントをUIScrollView
のサブビューに渡すことです。正直に言うと、なぜあなたがこれをしたいのか分かりません。特定のUIScrollView機能(...スクロールなど)を無効にしたい場合は、UserInteractionを無効にせずに十分に簡単に実行できます。
あなたの質問を理解した場合、サブビューに渡されるUIScrollViewandでタップイベントを処理する必要がありますか?いずれにせよ(ジェスチャーが何であれ)、あなたが探しているのはプロトコルメソッド gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: プロトコルUIGestureRecognizerDelegate
です。サブビューでは、ジェスチャレコグナイザーに関係なく、ジェスチャレコグナイザーにデリゲート(おそらく最初にUIGestureReconginzer
を設定するクラス)を設定します。上記のメソッドをオーバーライドして、YES
を返します。これで、このジェスチャは、ジェスチャを「盗んだ」可能性のある他のレコグナイザ(あなたの場合はタップ)とともに認識されます。この方法を使用すると、サブビューに特定の種類のジェスチャーのみを送信するようにコードを微調整したり、特定の状況でのみジェスチャーを送信したりすることもできます。それはあなたに多くの制御を与えます。メソッド、特にこの部分について必ず読んでください:
このメソッドは、gestureRecognizerまたはotherGestureRecognizerによるジェスチャの認識が、他のジェスチャ認識エンジンによるジェスチャの認識をブロックするときに呼び出されます。 YESを返すと、同時認識が保証されることに注意してください。一方、NOを返すと、他のジェスチャ認識エンジンのデリゲートがYESを返す可能性があるため、同時認識を防止することは保証されません。
もちろん、注意点があります:これはジェスチャー認識にのみ適用されます。そのため、touchesBegan:
、touchesEnded
などを使用してタッチを処理しようとすると、依然として問題が発生する可能性があります。もちろん、hitTest:
を使用して生のタッチイベントをサブビューに送信できますが、なぜですか? UIView
をビューにアタッチしてすべての機能を無料で入手できるのに、UIGestureRecognizer
のこれらのメソッドを使用してイベントを処理するのはなぜですか?標準のUIGestureRecognizer
が提供できない方法でタッチを処理する必要がある場合は、subclassUIGestureRecognizer
を使用してタッチを処理します。そうすれば、独自のカスタムタッチ処理とともにUIGestureRecognizer
のすべての機能を利用できます。開発者がUIGestureRecognizer
で使用するカスタムタッチ処理コードのほとんど(すべてではないにしても)を置き換えるために、AppleはUIView
を対象としています。 -再利用し、どのコードがどのタッチイベントを処理するかを緩和するときの対処がはるかに簡単になります。
これがあなたに役立つかどうかはわかりませんが、スクロールビューでダブルタップを処理し、シングルタップをサブビューに転送したいという同様の問題がありました。 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:として別のメソッドでこれを実装する必要があるかもしれません。
これが役立つことを願っています。
私はこれと同じ問題を抱えていましたが、スクロールビューがUIPageViewController
の中にあったため、少し異なる方法で処理する必要がありました。
cancelsTouchesInView
の各認識エンジンのUIScrollView
プロパティを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
}
タッチとスクロールの違いが必要な場合は、タッチが移動したかどうかをテストできます。これがタップの場合、touchHasBeenMovedは呼び出されず、これがタッチであると想定できます。
この時点で、ムーブメントが発生したかどうかを示すブール値を設定し、このブール値を他のメソッドの条件として設定できます。
私は外出中ですが、それがあなたが必要とするものであるならば、私は後でよりよく説明することができます。
100%正確ではなく、目的を達成するためのハック的な方法は、UIWindowをサブクラス化し、 void)sendEvent:(UIEvent *)event;をオーバーライドすることです。
簡単な例:
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
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];
}
2番目のレスポンダービューは-touchDidBegin:などを介してネイティブにタッチイベントを処理せず、SecondResponderWindowDelegateを実装する必要があるため、100%が期待したものに準拠していません。ただし、このハックを使用すると、追加のレスポンダーでのタッチイベントを処理できます。
このメソッドは、MITHIN KUMARの---(TapDetectingWindow に触発されて拡張されています。