web-dev-qa-db-fra.com

Exclure les sous-vues de UIGestureRecognizer

J'ai un UIView (la "vue conteneur") qui contient plusieurs "sous vues". Je souhaite ajouter un UITapGestureRecognizer à la vue du conteneur, de sorte qu'il soit activé lorsque je touche la région à l'intérieur de la vue du conteneur mais en dehors des sous-vues. 

Pour le moment, le fait de toucher n'importe où dans la vue du conteneur, y compris à l'intérieur des sous-vues, active la reconnaissance des gestes.

L'implémentation ressemble à ceci: Dans le contrôleur:

ContainerView *containerView = [[ContainerView alloc] initWithSubViews:array];
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc] initWithTarget:self action:@selector(someSelector)];
[containerView addGestureRecognizer:tap];
[self.view addSubView:containerView];

Dans ContainerView.m

-(id)initWithSubviews:(NSArray *)array {
    for (subView *s in array) {
        [self addSubView:s];
    }
    return self;
}

Je pense que le problème se produit parce que la reconnaissance de geste est ajoutée après les sous-vues. Si cela est vrai, la solution nécessiterait de diviser la méthode initWithSubViews en deux méthodes distinctes, ce que je préférerais éviter.

Je vous remercie

31
Awais Hussain

iOS 6 introduit une nouvelle fonctionnalité qui résout ce problème - une UIView (sous-vue) peut renvoyer NO à partir de gestureRecognizerShouldBegin: (reconnaissance de geste attachée à un aperçu). En fait, il s’agit déjà de la valeur par défaut pour certaines sous-classes UIView en ce qui concerne certains dispositifs de reconnaissance de gestes (par exemple, un UIButton en ce qui concerne un UITapGestureRecognizer attaché à un aperçu).

Voir mon livre sur ce sujet: http://www.apeth.com/iOSBook/ch18.html#_gesture_recognizers

10
matt

J'ai utilisé le moyen simple ci-dessous. Cela fonctionne parfaitement!

Implémentez la fonction UIGestureRecognizerDelegate, n'acceptez que les retouches sur superview, n'acceptez pas les retouches sur les sous-vues

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if (touch.view != _mySuperView) { // accept only touchs on superview, not accept touchs on subviews
        return NO;
    }

    return YES;
}
38
vietstone

J'ai réussi à le faire fonctionner en procédant comme suit:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHandler:)];

// ...

-(void) tapGestureHandler:(UITapGestureRecognizer *)sender {
    CGPoint point = [sender locationInView:sender.view];
    UIView *viewTouched = [sender.view hitTest:point withEvent:nil];
    if ([viewTouched isKindOfClass:[ThingIDontWantTouched class]]) {
        // Do nothing;
    } else {
        // respond to touch action
    }
}
12
Awais Hussain

N'oubliez pas d'ajouter et de définir la méthode déléguée

Dans votre fichier .h UIViewController, incluez ce délégué

@interface YourViewController: UIViewController<UIGestureRecogniserDelegate>

Ensuite, où vous créez votre objet tapGesture (dans viewDidLoad par exemple)

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(actionMethodYouWantToHappenOnTap)];  

    tap.delegate = self;  //Remember to set delegate! Otherwise the delegate method won't get called.

    [self.view addGestureRecognizer:tap];

N'oubliez pas de définir la méthode de déléguétap.delegate = self;, la méthode de délégué de tap va alors se déclencher.

Dans cette méthode, vous gérez le moment où vous souhaitez utiliser la reconnaissance des gestes tactiles. Dans mon code, je souhaitais qu'elle ignore un tapotement si une sous-vue particulière était visible.

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{

     if (!mySubview.hidden){
     return NO;   //This fired if said subview was visible, though whatever the condition of where and when you want tap to work, can be handled within this delegate method.
     }

    return YES;
}

J'espère que cela aide quelqu'un d'autre avec le même problème. Se souvenir de définir la méthode de délégué que j'ai trouvée était quelque chose qui était facilement négligé.

À votre santé

4
Jim Tierney

Beaucoup de réponses, en ajoutant une autre solution alternative. Initialisez la balise de vue des sous-vues pour lesquelles vous ne souhaitez pas recevoir de contacts, et procédez comme suit:

- (void)viewDidLoad {
    [super viewDidLoad];
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    tap.delegate = self;
    [self.view addGestureRecognizer:tap];
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if (touch.view.tag == 1) {
        return NO;
    }
    return YES;
}

-(void)handleTap:(id)sender
{
//Handle tap...
}

De cette façon, le geste tactile ne sera reçu que des vues dont vous avez besoin.

3
Koushik Ravikumar

Réponse mise à jour pour Swift 4

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

        return true
    }

Avant cela, assurez-vous que vous avez défini la propriété delegate sur votre objet.

1
abdul sathar

Si vous avez ajouté UITapGestureRecognizer pour ContainerView, il doit répondre dans son intégralité à ContainerView. Cela ne dérangera pas ses sous-vues.

Vérifiez l'emplacement du geste, s'il se trouve dans votre position de sous-vue, ignorez simplement les actions du geste.

- (void) tapGestureHandler:(UIGestureRecognizer *) gestureRecognizer {


    CGPoint tapPoint = [gestureRecognizer locationInView:nil]; 

   //check your tapPoint contains ur subview frame, skip.else do ur actions
}
0
Shamsudheen TK

AJOUTÉE: 

je viens de penser à quelque chose de mieux.

dans votre handler 

-(void)tapGestureHandler:(UITapGestureHandler)gesture

vérifiez si gesture.view est la superview. il retournera les sous-vues si les sous-vues sont exploitées.

=============================================== ==

Je suggérerais de passer outre à la superview.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

ceci est appelé pour déterminer si un geste tombe dans une vue

voir la réponse à cette question pour voir comment il se comporte par défaut.

Gestion des événements pour iOS - comment hitTest: withEvent: et pointInside: withEvent: sont liés?

vous pouvez vérifier si le point est dans l'une de ses sous-vues. si oui, retourne zéro, sinon retourne moi-même.

OR

dans chacune des sous-vues, ajoutez un outil de reconnaissance qui ne fait rien. la reconnaissance de mouvements d'une sous-vue annulera par défaut la reconnaissance de mouvements de sa vue d'ensemble. Je garderais un œil sur l'empreinte mémoire s'il y avait beaucoup de vues secondaires.

0
tzl