web-dev-qa-db-ja.com

特定のUIViewが他のUIViewの中でタッチされたかどうかを検出します

1つの大きなuiviewの上に3つのUIViewがあります。ユーザーが一番上のものに触れているかどうかを知りたいのですが、他のものは気にしません。 2番目のUIViewにはいくつかのボタンがあり、3番目のUIViewにはUITableがあります。

問題は、最初のビューでuserInteractionEngabledをオンにすると動作することですが、他のすべてのビューは、オフにしても同じように応答します。 self.viewでuserInteractionEnabledを無効にすると、どれも応答しません。また、touchesBeganデリゲートメソッドでどのビューがタッチされたかを検出することもできません。

私のコード:

UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 150)];
aView = userInteractionEnabled = YES;
[self.view addSubview:aView];

UIView *bView = [[UIView alloc] initWithFrame:CGRectMake(0, 150, 320, 50)];
bView.userInteractionEnabled = NO;
[self.view addSubview:bView];

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//This gets called for a touch anywhere
}
31
Rudiger

別のビュー内の特定のビューがタッチされたかどうかを確認するには、hitTestを使用できます。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;

タッチのカスタム実装で、タッチセットのすべてのタッチを確認し始めました。 hitTestメソッドのポイントは、次を使用して取得できます。

- (CGPoint)locationInView:(UIView *)view;

メソッド。ビューはsuperView(他のビューを含むビュー)です。

編集:高速カスタム実装は次のとおりです。

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    CGPoint locationPoint = [[touches anyObject] locationInView:self];
    UIView* viewYouWishToObtain = [self hitTest:locationPoint withEvent:event];
}

これがお役に立てば幸いです、ポール

62
Pawel

私の場合、検索したいUIViewはhotTestから返されませんでした。ただし、次のコードの方がうまく機能しました。

    CGPoint locationPoint = [[touches anyObject] locationInView:self.view];
    CGPoint viewPoint = [myImage convertPoint:locationPoint fromView:self.view];
    if ([myImage pointInside:viewPoint withEvent:event]) {
       do something
    }
27
rgadbois

これは、次の方法でも実行できます。

最初に、関心のあるビューでタッチの場所を見つけます。

CGPoint location = [touches.anyObject locationInView:viewYouCareAbout];

次に、ポイントがビューの境界内にあるかどうかを確認します。

BOOL withinBounds = CGRectContainsPoint(viewYouCareAbout.bounds, location);

次に、範囲内にある場合は、アクションを実行します。

これは、たとえばifステートメント内で直接使用できる1つのステートメントで実行できます。

CGRectContainsPoint(viewYouCareAbout.bounds, [touches.anyObject locationInView:viewYouCareAbout])
14
Ben Baron

スウィフトコード:

 override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            if touch.view == self.view {
                self.hideInView()
            } else {
                return
            }

        }
    }
13
Karthi Keyan

touchesBeganタッチされたビューが目的のものであることを確認し、ユーザーが特定のImageViewをタッチしたかどうかを特定しようとしたときに問題を解決しました。

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

    if ([touch view] ==  imageView ) {
        ... your code to handle the touch
    }
    ...

ビューの直径のコンテンツを調整する場合は、それに応じてビューのサイズを必ず変更してください。そうしないと、ビュー内のコンテンツがある領域でもタッチイベントが認識されます。私の場合、イメージが「IViewContentModeScaleAspectFit」で画像の比率を維持しようとしたとき、「ホワイトスペース」エリアで必要に応じて画像が調整されましたが、タッチイベントもキャッチされました。

8
Pichirichi

私のSwiftバージョンは、タッチがinfoViewで行われたことを確認します:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first, infoView.bounds.contains(touch.location(in: infoView))  else { return }
    print("touchesBegan") // Replace by your own code
} // touchesBegan
3
KerCodex

アプリケーションがタッチイベントを受け取ると、ヒットテストプロセスが開始され、イベントを受け取るビューを決定します。このプロセスは、ビュー階層のルート(通常はアプリケーションのウィンドウ)から始まり、タッチされている最前面のビューが見つかるまで、サブビューを前後に検索します。そのビューはヒットテストビューになり、タッチイベントを受け取ります。このプロセスに関係する各ビューは、最初にイベントの場所が境界内にあるかどうかをテストします。テストが成功した後でのみ、ビューはイベントをサブビューに渡し、さらにヒットテストを行います。したがって、ビューがタッチ下にあるが、親ビューの境界の外側にある場合、親ビューはイベントの場所のテストに失敗し、タッチイベントをビューに渡しません。

この問題の1つの解決策は、ビューが親ビューの境界内に収まるようにビュー階層のレイアウトを変更することです。何らかの理由で既存のレイアウトを維持する必要がある場合は、タッチイベントを無視しないように、親ビューのヒットテスト動作を変更できます。これは、親ビューのクラスの-(UIView *)hitTest:withEvent:メソッドをオーバーライドすることで実行できます

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    // Convert the point to the target view's coordinate system.
    // The target view isn't necessarily the immediate subview
    CGPoint pointForTargetView = [self.targetView convertPoint:point fromView:self];

    if (CGRectContainsPoint(self.targetView.bounds, pointForTargetView)) {

        // The target view may have its view hierarchy,
        // so call its hitTest method to return the right hit-test view
        return [self.targetView hitTest:pointForTargetView withEvent:event];
    }

    return [super hitTest:point withEvent:event]; }

この問題の他の考えられる原因は次のとおりです。

ビューまたはその親ビューのuserInteractionEnabledプロパティはNOに設定されます。アプリケーションは、endIgnoringInteractionEventsメソッドへの一致する呼び出しなしで、beginIgnoringInteractionEventsメソッドを呼び出しました。ビューをインタラクティブにしたい場合、userInteractionEnabledプロパティがYESに設定され、アプリケーションがユーザーイベントを無視しないようにする必要があります。

ビューがアニメーション中にタッチイベントを受け取らない場合、UIViewのアニメーションメソッドは通常、アニメーションの進行中にタッチイベントを無効にするためです。 UIViewアニメーションを開始するときにUIViewAnimationOptionAllowUserInteractionオプションを適切に構成することにより、この動作を変更できます。

別の解決策は

ビューに独自のカスタム実装を追加します

class CustomView: YourParentView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

        super.hitTest(point, with: event)
        return overlapHitTest(point: point, withEvent: event)
    }
}


extension UIView {

    func overlapHitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

        // 1
        if !self.isUserInteractionEnabled || self.isHidden || self.alpha == 0 {

            return nil
        }

        // 2
        var hitView: UIView? = self
        if !self.point(inside: point, with: event) {

            if self.clipsToBounds {

                return nil
            } else {

                hitView = nil
            }
        }

        // 3
        for subview in self.subviews.reversed() {

            let insideSubview = self.convert(point, to: subview)
            if let sview = subview.overlapHitTest(point: insideSubview, withEvent: event) {
                return sview
            }
        }
        return hitView
    }
}
0

これは私のために働いた:

func respondToGesture(gesture: UIGestureRecognizer) {
    let containsPoint = CGRectContainsPoint(targetView.bounds, gesture.locationInView(targetView))
}
0
Andy