web-dev-qa-db-ja.com

UITapGestureRecognizerはself.viewをタップしますが、サブビューは無視します

Self.view(UIViewCotrollerのビュー)をダブルタップすると、いくつかのコードを呼び出す機能を実装する必要があります。しかし、このビューには他のUIオブジェクトがあり、すべての認識オブジェクトをアタッチしたくないという問題があります。ビューでジェスチャーを作成する方法の下にこの方法を見つけ、それがどのように機能するかを知っています。現在、私はハンディキャップの前にいます。サブビューを無視してこの認識機能を作成するためにどの方法を選択するかです。何か案は?ありがとう。

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:doubleTap];
78

UIGestureRecognizerDelegateオブジェクト内でselfプロトコルを採用し、ビューを確認するために以下のメソッドを呼び出す必要があります。このメソッド内で、ビューをtouch.viewに対してチェックし、適切なブール値(はい/いいえ)を返します。このようなもの:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:yourSubView]) {
        return NO;
    }
    return YES;
}

編集:@Ianの回答もチェックしてください!

125
Ravi

別のアプローチは、タッチのビューがジェスチャービューであるかどうかを単に比較することです。子孫は条件を通過しないためです。素敵でシンプルなワンライナー:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}
90
Ian

また、Swiftバリアントの場合:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view.isDescendantOfView(yourSubView){
        return false
    }
    return true
}

isDescendantOfViewは、受信者が特定のビューのサブビューであるか、そのビューと同一であるかを示すBoolean値を返します。

19
Antoine

完全なSwiftソリューション(デリゲートを実装し、認識エンジンに設定する必要があります)):

class MyViewController: UIViewController UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onBaseTapOnly))
        doubleTapRecognizer.numberOfTapsRequired = 2
        doubleTapRecognizer.delegate = self
        self.view.addGestureRecognizer(doubleTapRecognizer)
    }

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
        if touch.view.isDescendantOfView(self.view){
            return false
        }
        return true
    }

    func onBaseTapOnly(sender: UITapGestureRecognizer) {
        if sender.state == .Ended {
            //react to tap
        }
    }
}
8
Arru

Swift 5およびiOS 12の場合、UIGestureRecognizerDelegateには gestureRecognizer(_:shouldReceive:) と呼ばれるメソッドがあります。gestureRecognizer(_:shouldReceive:)には次の宣言:

ジェスチャ認識エンジンがタッチを表すオブジェクトを受け取る必要があるかどうかをデリゲートに尋ねます。

_optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
_

以下の完全なコードは、gestureRecognizer(_:shouldReceive:)の可能な実装を示しています。このコードでは、ViewControllerのビューのサブビュー(imageViewを含む)をタップしても、printHello(_:)メソッドはトリガーされません。

_import UIKit

class ViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(printHello))
        tapGestureRecognizer.delegate = self
        view.addGestureRecognizer(tapGestureRecognizer)

        let imageView = UIImageView(image: UIImage(named: "icon")!)
        imageView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
        view.addSubview(imageView)

        // ⚠️ Enable user interaction for imageView so that it can participate to touch events.
        // Otherwise, taps on imageView will be forwarded to its superview and managed by it.
        imageView.isUserInteractionEnabled = true
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        // Prevent subviews of a specific view to send touch events to the view's gesture recognizers.
        if let touchedView = touch.view, let gestureView = gestureRecognizer.view, touchedView.isDescendant(of: gestureView), touchedView !== gestureView {
            return false
        }
        return true
    }

    @objc func printHello(_ sender: UITapGestureRecognizer) {
        print("Hello")
    }

}
_

gestureRecognizer(_:shouldReceive:)の代替実装は次のとおりです。

_func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return gestureRecognizer.view === touch.view
}
_

ただし、この代替コードは_touch.view_が_gestureRecognizer.view_のサブビューであるかどうかをチェックしないことに注意してください。

5
Imanou Petit

クリアSwift way

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}
2
Musa almatri

タッチしたCGPointを使用するバリアント(Swift 4.0)

class MyViewController: UIViewController, UIGestureRecognizerDelegate {

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {

// Get the location in CGPoint
    let location = touch.location(in: nil)

// Check if location is inside the view to avoid
    if viewToAvoid.frame.contains(location) {
        return false
    }

    return true
  }
}
2
CoachThys

上記のソリューションに加えて、User Interaction Enabledサブビューの。

enter image description here

1
MrDEV

gestureRecognizer API が次のように変更されていることに注意してください。

gestureRecognizer(_:shouldReceive :)

最初のパラメーターの外部ラベルのアンダースコア(スキップ)インジケーターに特に注意してください。

上記の例の多くを使用すると、イベントを受け取りませんでした。以下は、Swift(3+))の現在のバージョンで動作する例です。

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    var shouldReceive = false
    if let clickedView = touch.view {
        if clickedView == self.view {
            shouldReceive = true;
        }
    }
    return shouldReceive
}
1
Rico

子ビューでのジェスチャーを防止する必要がありました。唯一機能したのは、最初のビューを許可および保持し、次のすべてのビューでジェスチャーを防止することです。

   var gestureView: UIView? = nil

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if (gestureView == nil || gestureView == touch.view){
            gestureView = touch.view
            return true
        }
        return false
     }
0
Shay Moreno

Swift 4:

touch.viewはオプションになったため、@ Antoineの回答に基づいています。

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let touchedView = touch.view, touchedView.isDescendant(of: deductibleBackgroundView) {
        return false
    }
    return true
}
0