106 votes

UITapGestureRecognizer tape sur self.view mais ignore les sous-vues

J'ai besoin d'implémenter une fonction qui invoquera un certain code lorsque je double-clique sur la vue self.view (vue de l'écran d'accueil). UIViewCotroller ). Mais le problème est que j'ai d'autres objets UI sur cette vue et je ne veux pas attacher un objet recognizer à 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 savoir quelle voie choisir pour créer ce recognizer ignorant la subview. Des idées ? Merci.

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

173voto

Ravi Points 2629

Vous devez adopter le UIGestureRecognizerDelegate à l'intérieur du self et appelez la méthode ci-dessous pour vérifier la vue. Dans cette méthode, vérifiez votre vue par rapport à touch.view et retourner le bool approprié (Oui/Non). Quelque chose comme ceci :

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

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

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

126voto

Ian Points 10096

Une autre approche consiste à simplement comparer si la vue de la touche est la vue des gestes, car les descendants ne passeront pas la condition. Une solution simple et agréable :

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

23voto

Twan Points 265

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 un Boolean valeur indiquant si le récepteur est une sous-vue d'une vue donnée ou identique à cette vue.

18voto

Imanou Petit Points 43068

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

Demandez au délégué si un reconnaissant de gestes doit recevoir un objet représentant un toucher.

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

Le code complet ci-dessous montre une mise en œuvre possible pour gestureRecognizer(_:shouldReceive:) . Avec ce code, un tap sur une sous-vue de la ViewController (y compris imageView ) ne déclenchera pas le printHello(_:) méthode.

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 mise en œuvre alternative de gestureRecognizer(_:shouldReceive:) peut l'ê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 .

10voto

Arru Points 11

Solution rapide complète (le délégué doit être implémenté ET défini pour le(s) reconnaissant(s)) :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X