UIGestureRecognizer bloque la sous-vue pour gérer les events tactiles

J'essaie de comprendre comment cela se fait de la bonne façon . J'ai essayé de décrire la situation: entrez la description de l'image ici

UITableView un UITableView comme sous-vue d'un UIView . L' UIView répond à un tap- et pinchGestureRecognizer , mais dans ce cas, la vue de table cesse de réagir à ces deux gestes (elle réagit toujours aux balayages).

Je l'ai fait fonctionner avec le code suivant, mais ce n'est évidemment pas une bonne solution et je suis sûr qu'il y a un meilleur moyen. Ceci est mis dans l' UIView (l'aperçu):

 -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if([super hitTest:point withEvent:event] == self) { for (id gesture in self.gestureRecognizers) { [gesture setEnabled:YES]; } return self; } for (id gesture in self.gestureRecognizers) { [gesture setEnabled:NO]; } return [self.subviews lastObject]; } 

J'ai eu un problème très similaire et trouvé ma solution dans cette question SO . En résumé, définissez-vous en tant que délégué pour votre UIGestureRecognizer , puis vérifiez la vue ciblée avant d'autoriser votre reconnaissance à traiter le contact. La méthode de délégué pertinente est la suivante:

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 

Le blocage des events tactiles aux sous-vues est le comportement par défaut. Vous pouvez changer ce comportement:

 UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)]; r.cancelsTouchesInView = NO; [agentPicker addGestureRecognizer:r]; 

Une possibilité est de sous- -touchesBegan:withEvent: reconnaissance de gestes (si vous ne l'avez pas déjà fait) et de surcharger -touchesBegan:withEvent: telle sorte qu'il détermine si chaque touche a commencé dans une sous-vue exclue et appelle -ignoreTouch:forEvent: pour ce toucher si c'est le cas.

Évidemment, vous devrez également append une propriété pour garder une trace de la sous-vue exclue, ou peut-être mieux, un tableau de sous-vues exclues.

J'affichais une sous-vue déroulante qui avait sa propre vue de table. Par conséquent, touch.view parfois des classs comme UITableViewCell . J'ai dû traverser la (les) super-class (s) pour m'assurer que c'était la sous-class que je pensais que c'était:

 -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { UIView *view = touch.view; while (view.class != UIView.class) { // Check if superclass is of type dropdown if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own NSLog(@"Is of type dropdown; returning NO"); return NO; } else { view = view.superview; } } return YES; } 

Il est possible de faire sans hériter de n'importe quelle class.

vous pouvez vérifier gestureRecognizers dans le sélecteur de callback de gesture

Si view.gestureRecognizers ne contient pas votre gestureRecognizer, ignorez-le

par exemple

 - (void)viewDidLoad { UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; singleTapGesture.numberOfTapsRequired = 1; } 

vérifier view.gestureRecognizers ici

 - (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer { UIEvent *event = [[UIEvent alloc] init]; CGPoint location = [gestureRecognizer locationInView:self.view]; //check actually view you hit via hitTest UIView *view = [self.view hitTest:location withEvent:event]; if ([view.gestureRecognizers containsObject:gestureRecognizer]) { //your UIView //do something } else { //your UITableView or some thing else... //ignore } } 

J'ai créé une sous-class UIGestureRecognizer conçue pour bloquer tous les reconnaisseurs de gestes attachés à des aperçus d'une vue spécifique.

Cela fait partie de mon projet WEPopover. Vous pouvez le find ici .

Je faisais aussi un popover et voici comment je l'ai fait

 func didTap(sender: UITapGestureRecognizer) { let tapLocation = sender.locationInView(tableView) if let _ = tableView.indexPathForRowAtPoint(tapLocation) { sender.cancelsTouchesInView = false } else { delegate?.menuDimissed() } } 

Vous pouvez l'éteindre et le rallumer … dans mon code j'ai fait quelque chose comme ça car j'avais besoin de l'éteindre quand le keyboard ne s'affichait pas, vous pouvez l'appliquer à votre situation:

appelez ceci est viewdidload etc:

 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil]; [center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil]; 

puis créez les deux methods:

 -(void) notifyShowKeyboard:(NSNotification *)inNotification { tap.enabled=true; // turn the gesture on } -(void) notifyHideKeyboard:(NSNotification *)inNotification { tap.enabled=false; //turn the gesture off so it wont consume the touch event } 

Qu'est-ce que cela fait, c'est désactiver le robinet. Je devais tourner tap dans une variable d'instance et la libérer en dealloc cependant.

implémenter un délégué pour tous les reconnaisseurs de parentView et mettre la méthode gestureRecognizer dans le délégué qui est responsable du triggersment simultané des reconnaisseurs:

 func gestureRecognizer(UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool { if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) { return true } else { return false } 

}

Vous pouvez utiliser les methods d'échec si vous voulez que les enfants soient déclenchés mais pas les identificateurs parents:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate

En s'appuyant sur @Pin Shih Wang réponse . Nous ignorons tous les taps autres que ceux de la vue contenant le reconnaisseur de gestes de frappe. Tous les taps sont transmis à la hiérarchie de vue comme d'habitude car nous avons défini tapGestureRecognizer.cancelsTouchesInView = false . Voici le code dans Swift3 / 4:

 func ensureBackgroundTapDismissesKeyboard() { let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tapGestureRecognizer.cancelsTouchesInView = false self.view.addGestureRecognizer(tapGestureRecognizer) } @objc func handleTap(recognizer: UIGestureRecognizer) { let location = recognizer.location(in: self.view) let hitTestView = self.view.hitTest(location, with: UIEvent()) if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) { // I dismiss the keyboard on a tap on the scroll view // REPLACE with own logic self.view.endEditing(true) } }