web-dev-qa-db-fra.com

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

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

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];
}
78
andershqst

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
180
Justin

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];
102
Clive Paterson

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;
}
5
joslinm

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.

4
Caleb

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)
    }
}
3
Nick Ager

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
    }
}
2
Pin Shih Wang

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 .

1
Werner Altewischer

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

0
user7270881

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. 

0
j2emanue

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()
    }
}
0
Maria