web-dev-qa-db-fra.com

UITapGestureRecognizer tapez sur self.view mais ignorez les sous-vues

Je dois implémenter une fonctionnalité qui invoquera du code lorsque je double frappe sur le fichier self.view (vue de UIViewCotroller). Mais le problème est que j'ai un autre objet d'interface utilisateur sur cette vue et que je ne souhaite pas attacher d'objet de reconnaissance à tous. J'ai trouvé cette méthode ci-dessous comment faire un geste sur ma vue et je sais comment cela fonctionne. En ce moment, je suis devant le handicap de quelle manière choisir pour créer cette reconnaissance en ignorant la sous-vue. Des idées? Merci.

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

Vous devez adopter le protocole UIGestureRecognizerDelegate à l'intérieur de l'objet self et appeler la méthode ci-dessous pour vérifier la vue. Dans cette méthode, vérifiez votre vue par rapport à touch.view et renvoyez le booléen approprié (Oui/Non). Quelque chose comme ça:

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

Edit: S'il vous plaît, vérifiez également la réponse de @ Ian! 

111
Ravi

Une autre approche consiste simplement à comparer si la vue tactile est la vue gestuelle, car les descendants ne passeront pas la condition. Un beau, simple one-liner:

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

Et pour la variante Swift:

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

Bon à savoir, isDescendantOfView renvoie une valeur Boolean indiquant si le destinataire est une sous-vue d'une vue donnée ou identique à cette vue. 

16
Antoine

Solution Swift complète (le délégué doit être implémenté ET défini pour le ou les dispositifs de reconnaissance):

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

Avec iOS 11 et Swift 4.2, UIGestureRecognizerDelegate dispose d'une méthode appelée gestureRecognizer(_:shouldReceive:) . gestureRecognizer(_:shouldReceive:) a la déclaration suivante:

Demandez au délégué si un dispositif de reconnaissance de geste doit recevoir un objet représentant une touche.

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

Le code complet ci-dessous montre une implémentation possible pour gestureRecognizer(_:shouldReceive:). Avec ce code, un tap sur une sous-vue de la vue ViewController (y compris imageView) ne déclenchera pas la méthode 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")
    }

}

Une autre implémentation de gestureRecognizer(_:shouldReceive:) peut être:

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

Notez cependant que ce code alternatif ne vérifie pas si touch.view est une sous-vue de gestureRecognizer.view.

4
Imanou Petit

Clair Swift Way

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

Variante utilisant CGPoint que vous touchez (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

En plus des solutions ci-dessus, n'oubliez pas de vérifier User Interaction Enabled de votre sous-vue.

 enter image description here

1
MrDEV

Notez que l'API gestureRecognizer a été modifiée en:

gestureRecognizer (_: shouldReceive :)

Prenez particulièrement note de l’indicateur de soulignement (saut) de l’étiquette externe du premier paramètre. 

En utilisant plusieurs des exemples fournis ci-dessus, je ne recevais pas l'événement. Vous trouverez ci-dessous un exemple qui fonctionne pour les versions actuelles de 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

Je devais empêcher le geste sur la vue de l'enfant. La seule chose qui a fonctionné est d’autoriser et de conserver la première vue et d’empêcher les gestes dans toutes les vues suivantes:

   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 est maintenant optionnel, donc basé sur la réponse de @ Antoine

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