J'essaie de comprendre comment cela se fait de la bonne façon . J'ai essayé de décrire la situation:
J'ajoute une UITableView
en tant que sous-vue d'une UIView
. UIView
répond à un tap et pinchGestureRecognizer
, mais lorsque cela se produit, la vue 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 solution intéressante et je suis sûr qu'il existe un meilleur moyen. Ceci est mis dans la UIView
(la superview):
-(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 j'ai trouvé la solution dans cette SO question . En résumé, définissez-vous comme délégué de votre UIGestureRecognizer
, puis vérifiez la vue ciblée avant de permettre à votre outil de reconnaissance de traiter le contact. La méthode de délégué appropriée est:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
Le comportement par défaut des événements tactiles sur les 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];
J'affichais une sous-vue déroulante qui avait sa propre table. En conséquence, le touch.view
renverrait parfois des classes telles que UITableViewCell
. Je devais passer en revue les super-classes pour m'assurer que c'était bien la sous-classe que je croyais être:
-(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;
}
Une possibilité consiste à sous-classer votre identificateur de geste (si vous ne l'avez pas déjà fait) et à remplacer -touchesBegan:withEvent:
de sorte qu'il détermine si chaque contact a commencé dans une sous-vue exclue et appelle -ignoreTouch:forEvent:
pour ce contact s'il l'a fait.
De toute évidence, vous devrez également ajouter une propriété pour garder une trace de la sous-vue exclue, ou peut-être mieux d'un tableau de sous-vues exclues.
S'appuyant sur @Pin Shih Wang répondre . Nous ignorons tous les taps autres que ceux de la vue contenant le programme de reconnaissance de gestes tap Tous les taps sont transférés à la hiérarchie de vues normalement, comme 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)
}
}
Il est possible de ne pas hériter d'une classe.
vous pouvez vérifier gestureRecognizers dans le sélecteur de rappel de geste
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érifiez 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-classe UIGestureRecognizer conçue pour bloquer toutes les fonctions de reconnaissance de geste attachées à une vue d'ensemble d'une vue spécifique.
Cela fait partie de mon projet WEPopover. Vous pouvez le trouver ici .
implémentez un délégué pour tous les identifiants du parentView et placez la méthode gestureRecognizer dans le délégué responsable du déclenchement simultané des identifiants:
func gestureRecognizer(UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool {
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) {
return true
} else {
return false
}
}
Vous pouvez utiliser les méthodes d’échec si vous voulez que les enfants soient déclenchés mais pas les reconnaisseurs parents:
https://developer.Apple.com/reference/uikit/uigesturerecognizerdelegate
Vous pouvez l'éteindre et l'activer .... dans mon code, j'ai fait quelque chose comme ceci car je devais l'éteindre quand le clavier n'était pas affiché, vous pouvez l'appliquer à votre situation:
appeler c'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 méthodes:
-(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
}
Cela désactive le robinet. Je devais cependant exploiter la variable d'instance et la publier dans dealloc.
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()
}
}