recognizer ios uitapgesturerecognizer

ios - UITapGestureRecognizer toca en self.view pero ignora las subvistas



tap gesture recognizer swift 4 (9)

Además de las soluciones anteriores, no olvide verificar User Interaction Enabled en su subvista.

Necesito implementar una función que invoque algún código cuando haga doble clic en self.view (vista de UIViewCotroller ). Pero el problema es que tengo otro objeto UI en esta vista y no quiero adjuntar ningún objeto de reconocimiento a todos ellos. Encontré este método debajo de cómo hacer gestos en mi vista y sé cómo funciona. En este momento estoy frente a un hándicap, qué forma elegir para crear este reconocedor ignorando la subvista. ¿Algunas ideas? Gracias.

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


Con iOS 11 y Swift 4.2, UIGestureRecognizerDelegate tiene un método llamado gestureRecognizer(_:shouldReceive:) . gestureRecognizer(_:shouldReceive:) tiene la siguiente declaración:

Pregunte al delegado si un reconocedor de gestos debería recibir un objeto que represente un toque.

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

El código completo a continuación muestra una posible implementación para el gestureRecognizer(_:shouldReceive:) . Con este código, un toque en una subvista de la vista de ViewController (incluida imageView ) no activará el método 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") } }

Una implementación alternativa de gestureRecognizer(_:shouldReceive:) puede ser:

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

Sin embargo, touch.view cuenta que este código alternativo no comprueba si touch.view es una subvista de touch.view .


Debe adoptar el protocolo UIGestureRecognizerDelegate dentro del objeto self y llamar al método siguiente para verificar la vista. Dentro de este método, verifique su vista con touch.view y devuelva el bool apropiado (Sí / No). Algo como esto:

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

Edición: Por favor, también compruebe la respuesta de @ Ian!


Otro enfoque es comparar solo si la vista del toque es la vista de gestos, porque los descendientes no pasarán la condición. Un bonito, sencillo de una sola línea:

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


Solución Swift completa (el delegado debe implementarse Y establecerse para los reconocedores):

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


Tenga en cuenta que la API de gestor de reconocimiento ha cambiado a:

gestoReconocer (_: debería Recibir :)

Tome nota en particular del indicador de subrayado (salto) para la etiqueta externa del primer parámetro.

Usando muchos de los ejemplos proporcionados anteriormente, no estaba recibiendo el evento. A continuación se muestra un ejemplo que funciona para las versiones actuales 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 }


Y para la variante Swift:

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

Es bueno saberlo, isDescendantOfView devuelve un valor Boolean que indica si el receptor es una subvista de una vista determinada o es idéntico a esa vista.


Swift 4:

touch.view ahora es opcional, por lo que basado en la respuesta de @ Antoine:

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


Variante utilizando CGPoint que tocas (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 } }