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


95

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

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

1
わかりませんが、認識機能でcancelsTouchesInViewをNOに設定してみましたか?そう[doubleTap setCancelsTouchesInView:NO];
JDx 2013

回答:


144

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

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

編集:@Ianの回答も確認してください!

スウィフト5

// MARK: UIGestureRecognizerDelegate methods, You need to set the delegate of the recognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
     if touch.view?.isDescendant(of: tableView) == true {
        return false
     }
     return true
}

もう1つの質問私のビューには、機能するいくつかのボタンがあります。どうすればそれらにいくつかの優先順位を設定できますか?
Matrosov Alexander

ボタンに独自のジェスチャ-[UIGestureRecognizer requireGestureRecognizerToFail:]レコグナイザーがある場合(可能性が高い)、ボタンの内部を調べて取得し、ドキュメントを読みますが、変更しなくても機能する可能性があります
bshirley

これを何時間も探していました!ありがとう!;)
MasterRazer 2013年

あなたの場合はif、テストが失敗し、あなたの実装がBOOLを返すことに失敗しました。適切なスタイルの場合は、ifブロック(を使用{ })またはelseブランチ内でYESを返します。おかげで、しかし、私は少し読書を保存しました。
RobP

2
ビューはそれ自体の子孫なのでこれは機能しません...(全体的なアプローチは問題ありませんが、他の回答を参照してください)
Ixx

107

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

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

2
これは、受け入れられた答えよりも私にとってはうまく機能しています。はるかに簡潔です。
Suman Roy

1
@Ianこのメソッドは、ビューをタップしても呼び出されません。
Praburaj 2017

1
すばらしい簡潔な答えです!
scurioni 2017年

受け入れられた答えはうまくいきませんでしたが、これは私にとって完璧に機能しました。
Shalin Shah

@Prabuジェスチャーレコグナイザーのデリゲートを前に設定する必要があるため適切に
Kubba

23

Swiftバリアントの場合:

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

知っておきたいことですが、レシーバが特定のビューのサブビューであるか、そのビューと同一であるかを示す値をisDescendantOfView返しBooleanます。


1
クラスでUIGestureRecognizerDelegateを設定することを忘れないでください!
Fox5150

4
ifステートメントを実行する代わりに、これを返すことはできませんか?return!touch.view.isDescendant(of:gestureRecognizer.view)また、
Swift

12

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


10

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

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

6

タッチした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
  }
}

4

クリアスウィフトウェイ

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

iOS 13では、少なくともswipeGestureRecognizerでは呼び出されません。
Chewie The Chorkie

これはイアンの答えとどう違うのですか?
sweetfa

3

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
}

2

上記の解決策に加えて、サブビューを確認することを忘れないUser Interaction Enabledでください。

ここに画像の説明を入力してください


2

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

   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
     }

1

スウィフト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

「ダブルタップ認識機能」をボタンや他のコントロールと競合させたくない場合は、次のように設定selfUIGestureRecognizerDelegateて実装できます。

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.