Reconnaissance des gestes et actions des boutons

99

J'ai une hiérarchie de vues qui ressemble à ceci:

UIView (A)
UIView > UIImageView
UIView > UIView (B)
UIView > UIView (B) > Rounded Rect Button
UIView > UIView (B) > UIImageView
UIView > UIView (B) > UILabel

J'ai attaché un ou plusieurs reconnaisseurs de gestes à mon UIView (B). Le problème auquel je suis confronté est que je n'obtiens aucune action pour le bouton Rounded Rect qui se trouve à l'intérieur de l'UIView (B). Le module de reconnaissance de gestes singleTap capture / remplace l'événement Touch Up Inside du bouton.

Comment puis-je le faire fonctionner? Je pensais que la hiérarchie de la chaîne de répondeurs s'assurera que l'événement tactile de bouton aura la préférence, et il sera déclenché! Qu'est-ce que je rate?

Voici un code associé:

#pragma mark -
#pragma mark View lifecycle (Gesture recognizer setup)

- (void)viewDidLoad {
    [super viewDidLoad];

    // double tap gesture recognizer
    UITapGestureRecognizer *dtapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapGestureRecognizer:)];
    dtapGestureRecognize.delegate = self;
    dtapGestureRecognize.numberOfTapsRequired = 2;
    [self.viewB addGestureRecognizer:dtapGestureRecognize];

    // single tap gesture recognizer
    UITapGestureRecognizer *tapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapGestureRecognizer:)];
    tapGestureRecognize.delegate = self;
    tapGestureRecognize.numberOfTapsRequired = 1;
    [tapGestureRecognize requireGestureRecognizerToFail:dtapGestureRecognize];
    [self.viewB addGestureRecognizer:tapGestureRecognize];

    // add gesture recodgnizer to the grid view to start the edit mode
    UILongPressGestureRecognizer *pahGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGestureRecognizerStateChanged:)];
    pahGestureRecognizer.delegate = self;
    pahGestureRecognizer.minimumPressDuration = 0.5;
    [self.viewB addGestureRecognizer:pahGestureRecognizer];

    [dtapGestureRecognize release];
    [tapGestureRecognize release];
    [pahGestureRecognizer release];
}

#pragma mark -
#pragma mark Button actions

- (IBAction)buttonTouchUpInside:(id)sender {
    NSLog(@"%s, %@", __FUNCTION__, sender);
}

#pragma mark -
#pragma mark Gesture recognizer actions


- (void)singleTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
    NSLog(@"%s", __FUNCTION__);
}

- (void)doubleTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
    NSLog(@"%s", __FUNCTION__);
}

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

    switch (gestureRecognizer.state) {

        case UIGestureRecognizerStateEnded: {
            NSLog(@"%s", __FUNCTION__);

            break;
        }
        default:
            break;
    }
}
Mustafa
la source
Le moyen le plus simple est de définir cancelsTouchesInView = false
Bagusflyer

Réponses:

176

Dans la méthode "shouldReceiveTouch", vous devez ajouter une condition qui renverra NON si le toucher est dans le bouton.

Ceci provient de l' exemple Apple SimpleGestureRecognizers .

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

    // Disallow recognition of tap gestures in the segmented control.
    if ((touch.view == yourButton)) {//change it to your condition
        return NO;
    }
    return YES;
}

j'espère que ça aidera

Éditer

Comme Daniel l'a noté, vous devez vous y conformer pour UIGestureRecognizerDelegateque cela fonctionne.

Shani

Shannoga
la source
1
Ah !!! Oui, j'ai oublié cette méthode -gestureRecognizer: shouldReceiveTouch:. Merci de m'avoir pointé dans la bonne direction. Bien que cela résout mon problème, mais je ne sais toujours pas pourquoi la hiérarchie des répondeurs ne fonctionne pas dans ce cas. Cela devrait probablement le faire, mais ce n'est pas géré par Apple.
Mustafa
2
Le comportement de UIGestureRecognizer est clairement décrit comme distinct de la chaîne de répondeurs «normale». Il s'agit d'une décision de conception d'Apple.
Sylvain G.
11
N'oubliez pas de vous conformer au protocole UIGestureRecognizerDelegate et de définir le délégué de UIGestureRecognizer sur cet objet.
Daniel
2
Pour une utilisation plus large, considérez une variante des clauses de test de Ramesh ou de flypig. Dans mon cas, par exemple, je me suis retrouvé avec la ligne: if ( [touch.view isKindOfClass:[UIControl class]] || [[touch.view superview] isKindOfClass:[UITableViewCell class]] ) {... Notez que les cellules tableview sont "touchées" dans leur contentView, qui est membre d'une classe privée, donc là nous vérifions la classe de superview à la place.
clozach
Merci pour la réponse! Dans mon cas, j'ai rencontré le même problème mais je ne l'ai découvert que jusqu'à ce que je l'ai testé sur l'appareil réel. L'exécution de l'application dans le simulateur sans utiliser UIGestureDelegate comme indiqué dans cette réponse a fonctionné comme prévu, mais de manière incorrecte.
Luis Artola
51

J'ai aussi eu le même problème, puis j'ai essayé avec

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

    if ([touch.view isKindOfClass:[UIButton class]]) {      //change it to your condition
        return NO;
    }
    return YES;
}

Cela fonctionne maintenant parfaitement .........

Ramesh Chandran A
la source
4
J'ai seulement manqué lemyGestureRecognizer.delegate = self;
zero3nna
20

De manière générale, nous utilisons la méthode déléguée ci-dessous pour éviter le toucher dans toutes sortes d'UIControls:

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

Remarque: NE faites PAS cette vérification (vérifiez le type de classe Recognizer.view) le gestureRecognizerShouldBegin, cela ne fonctionnera pas.

flypig
la source
11

Voici une version Swift 3.0 :

extension UIViewController: UIGestureRecognizerDelegate {

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if touch.view is UIButton {
        return false
    }
    return true
}

N'oubliez pas de:

Make your tapper object delegate to self (e.g: tapper.delegate = self)

Marcelo Gracietti
la source
6

Voici une version Swift:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if (touch.view!.isKindOfClass(UIButton)) {
        return false
    }
    return true
}

N'oubliez pas de:

  1. Rendre votre classe conforme à UIGestureRecognizerDelegate
  2. Faites votre délégué objet Tapper à l' auto (par exemple: tapper.delegate = self)
rsc
la source
5

La meilleure solution est à mon avis d'utiliser l'extrait de code ci-dessous:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    CGPoint touchLocation = [touch locationInView:self.view];
    return !CGRectContainsPoint(self.menuButton.frame, touchLocation);
}
Solitaire
la source