195 votes

UIButton à l'intérieur d'une vue qui a un UITapGestureRecognizer

J'ai une vue avec un UITapGestureRecognizer . Ainsi, lorsque je tape sur la vue, une autre vue apparaît au-dessus de cette vue. Cette nouvelle vue comporte trois boutons. Lorsque j'appuie sur l'un de ces boutons, je n'obtiens pas l'action des boutons, mais seulement l'action du geste de toucher. Je ne suis donc plus en mesure d'utiliser ces boutons. Que puis-je faire pour que les événements soient transmis à ces boutons ? Ce qui est étrange, c'est que les boutons sont toujours mis en évidence.

Je ne peux pas simplement supprimer l'UITapGestureRecognizer après avoir reçu son tap. Parce qu'avec lui, la nouvelle vue peut aussi être supprimée. Cela signifie que je veux un comportement comme les contrôles de la vidéo en plein écran .

263voto

Kevin Ballard Points 88866

Vous pouvez définir votre contrôleur ou votre vue (celui ou celle qui crée la reconnaissance des gestes) en tant que délégué de la méthode de reconnaissance des gestes. UITapGestureRecognizer . Ensuite, dans le délégué, vous pouvez implémenter -gestureRecognizer:shouldReceiveTouch: . Dans votre implémentation, vous pouvez tester si le toucher appartient à votre nouvelle sous-vue et, si c'est le cas, demander à la reconnaissance gestuelle de l'ignorer. Quelque chose comme ce qui suit :

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // test if our control subview is on-screen
    if (self.controlSubview.superview != nil) {
        if ([touch.view isDescendantOfView:self.controlSubview]) {
            // we touched our control surface
            return NO; // ignore the touch
        }
    }
    return YES; // handle the touch
}

0 votes

Dans mon fichier d'en-tête, ma vue implémente UIGestoreRegognizerDelegate, et dans mon .m, j'ai ajouté votre code ci-dessus. Le robinet n'entre jamais dans cette méthode, il va directement à mon gestionnaire. Avez-vous une idée ?

1 votes

@kmehta vous avez très probablement oublié de définir la propriété du délégué UIGestureRecognizer.

0 votes

Cette réponse peut-elle être généralisée pour traiter tous les cas où une IBAction est impliquée ?

160voto

cdasher Points 2225

Pour faire suite à la réponse de Casey à celle de Kevin Ballard :

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        if ([touch.view isKindOfClass:[UIControl class]]) {
            // we touched a button, slider, or other UIControl
            return NO; // ignore the touch
        }
    return YES; // handle the touch
}

Cela permet de faire fonctionner tous les types de contrôles d'entrée utilisateur tels que les boutons, les curseurs, etc.

1 votes

C'est une solution flexible qui peut être accompagnée de setCancelsTouchesInView = NO pour ne pas déclencher le geste de tapotement lors de l'interaction avec les commandes. Cependant, vous pouvez l'écrire mieux : return ![touch.view isKindOfClass:[UIControl class]];

2 votes

Solution Swift 4+ : return !(touch.view is UIControl)

108voto

ejazz Points 1174

J'ai trouvé cette réponse ici : enlace

Vous pouvez également utiliser

tapRecognizer.cancelsTouchesInView = NO;

Ce qui empêche la reconnaissance des robinets d'être la seule à attraper tous les robinets.

MISE À JOUR - Michael mentionne le lien vers la documentation décrivant cette propriété : cancelsTouchesInView

9 votes

Cela devrait être la réponse acceptée par l'IMO. Cela permet à chaque sous-vue de gérer le clic de manière autonome et empêche la vue à laquelle est attaché un tabrecognizer de "détourner" le clic. Il n'est pas nécessaire d'implémenter un délégué si tout ce que vous voulez faire est de rendre une vue cliquable (pour démissionner le premier répondant / cacher le clavier sur les champs de texte, etc) génial !

4 votes

Cela devrait être la réponse acceptée. Cette réponse m'a conduit à lire le Documentation Apple correctement, ce qui indique clairement que le dispositif de reconnaissance des gestes empêchera les sous-vues de recevoir les événements reconnus. sauf si vous faites ça.

1 votes

Cela fonctionnait auparavant, mais maintenant cela ne fonctionne plus pour moi peut-être parce que j'ai un scrollView sous le bouton ? J'ai dû implémenter le délégué comme dans les réponses ci-dessus.

72voto

Casey Points 1251

Pour faire suite à la réponse de Kevin Ballard, j'ai eu le même problème et j'ai fini par utiliser ce code :

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

Cela a le même effet mais cela fonctionnera sur n'importe quel UIButton à n'importe quelle profondeur de vue (mon UIButton était à plusieurs vues de profondeur et le délégué de UIGestureRecognizer n'avait pas de référence vers lui).

6 votes

Je ne veux pas faire le con ici, mais c'est vraiment OUI et NON. Veuillez utiliser les conventions Cocoa. TRUE/FALSE/NULL est pour la couche CoreFoundation.

0 votes

D'après ce que j'ai lu, YES et NO ont en fait été créés pour la lisibilité. la lisibilité est subjective. elle découle des conventions de dénomination des méthodes et des propriétés, dont tout le monde ne se préoccupe pas outre mesure. si vous voulez une adhésion stricte à la convention de dénomination, alors oui, utilisez YES et NO pour n'importe quel NSWhatever. cependant, je dirai que si vous interrogez les développeurs, vous obtiendrez des avis partagés sur ce qui est le plus lisible [button setHidden:YES] ; -ou- [button setHidden:TRUE] ;

1 votes

Je suppose que ce que j'essaie de dire, c'est que si le principe des conventions de nommage est la lisibilité, je pense qu'il est difficile de nier que c'est subjectif dans certains cas. si vous êtes un puriste, vous ne comprendrez pas cette déclaration.

8voto

Sam Budda Points 3446

Ces réponses étaient incomplètes. J'ai dû lire plusieurs articles pour savoir comment utiliser cette opération booléenne.

Dans votre fichier *.h, ajoutez ceci

@interface v1ViewController : UIViewController <UIGestureRecognizerDelegate>

Dans votre fichier *.m, ajoutez ceci

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

    NSLog(@"went here ...");

    if ([touch.view isKindOfClass:[UIControl class]])
    {
        // we touched a button, slider, or other UIControl
        return NO; // ignore the touch
    }
    return YES; // handle the touch
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    //tap gestrure
    UITapGestureRecognizer *tapGestRecog = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenTappedOnce)];
    [tapGestRecog setNumberOfTapsRequired:1];
    [self.view addGestureRecognizer:tapGestRecog];

// This line is very important. if You don't add it then your boolean operation will never get called
tapGestRecog.delegate = self;

}

-(IBAction) screenTappedOnce
{
    NSLog(@"screenTappedOnce ...");

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X