23

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

あるいは、タップ ジェスチャがサブビューに転送されていれば、すべて問題ありません。

多くの人がこの一般的な分野で苦労しています。関連する多くの質問のいくつかを次に示します。

UIScrollView はサブビューからタッチ
を盗む方法 UIScrollView からタッチを盗む方法は?
UIScrollView でスクロールをキャンセルする方法

ちなみに、スクロール ビューで hitTest:withEvent: をオーバーライドすると、userInteractionEnabled が YES である限り、タッチが表示されます。しかし、それは私の問題を本当に解決しません。

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

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

4

5 に答える 5

3

私はこれと同じ問題を抱えていましたが、スクロールビューが 内UIPageViewControllerにあったため、少し異なる方法で処理する必要がありました。

cancelsTouchesInViewの各レコグナイザーのプロパティを false に変更することで、UIScrollView内のボタンへのタッチを受け取ることができました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
}
于 2016-04-12T03:25:57.313 に答える
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: として別のメソッドでこれを実装する必要がある場合があります。

これが役立つことを願っています。

于 2012-07-17T15:36:31.710 に答える
0

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

期待していたものに 100% 準拠しているわけではありません。これは、セカンド レスポンダー ビューが -touchDidBegin: などを介してネイティブにタッチ イベントを処理せず、SecondResponderWindowDelegate を実装する必要があるためです。ただし、このハックにより、追加のレスポンダーでタッチ イベントを処理できます。

このメソッドは、MITHIN KUMAR のTapDetectingWindowに触発され、拡張されています。

于 2012-07-19T01:11:47.067 に答える
0

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

この時点で、モーションが発生したかどうかを示すブール値を設定し、このブール値を他のメソッドの条件として設定できます。

私は外出中ですが、それが必要な場合は、後で詳しく説明できます。

于 2012-07-14T16:34:12.670 に答える