Acciones de botones y reconocimiento de gestos

99

Tengo una jerarquía de vistas que se parece a esto:

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

Adjunté reconocedores de gestos a mi UIView (B). El problema al que me enfrento es que no obtengo ninguna acción para el botón Rect redondeado que está dentro de UIView (B). El reconocedor de gestos singleTap captura / anula el evento Touch Up Inside del botón.

¿Cómo puedo hacer que funcione? Pensé que la jerarquía de la cadena de respuesta se asegurará de que se dé preferencia al evento de toque de botón, ¡y se activará! ¿Qué me estoy perdiendo?

Aquí hay un código relacionado:

#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
fuente
La forma más sencilla es establecer cancelsTouchesInView = false
Bagusflyer

Respuestas:

176

En el método "shouldReceiveTouch" debe agregar una condición que devolverá NO si el toque está en el botón.

Esto es del ejemplo de 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;
}

espero que ayude

Editar

Como señaló Daniel, debes ajustarte a UIGestureRecognizerDelegateello para que funcione.

shani

Shannoga
fuente
1
Ah !!! Sí, me olvidé de este método -gestureRecognizer: shouldReceiveTouch :. Gracias por señalarme en la dirección correcta. Aunque esto resuelve mi problema, todavía no sé por qué la jerarquía de respondedores no funciona en este caso. Probablemente debería, pero no está siendo manejado por Apple.
Mustafa
2
El comportamiento de UIGestureRecognizer se describe claramente como separado de la cadena de respuesta "normal". Esta es una decisión de diseño de Apple.
Sylvain G.
11
Recuerde cumplir con el protocolo UIGestureRecognizerDelegate y establecer el delegado de UIGestureRecognizer en ese objeto.
Daniel
2
Para un uso más amplio, considere alguna variante de las cláusulas de prueba de Ramesh o flypig. En mi caso, por ejemplo, terminé con la línea: if ( [touch.view isKindOfClass:[UIControl class]] || [[touch.view superview] isKindOfClass:[UITableViewCell class]] ) {... Tenga en cuenta que las celdas de la vista de tabla se "tocan" en su vista de contenido, que es miembro de una clase privada, así que allí verificamos la clase de la supervista.
clozach
¡Gracias por la respuesta! En mi caso, me encontré con el mismo problema, pero lo descubrí solo hasta que probé en el dispositivo real. La ejecución de la aplicación en el simulador sin usar UIGestureDelegate como se indica en esta respuesta funcionó como se esperaba, aunque de manera incorrecta.
Luis Artola
51

También tuve el mismo problema, luego intenté con

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

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

Ahora está funcionando perfectamente .........

Ramesh Chandran A
fuente
4
Solo me perdí elmyGestureRecognizer.delegate = self;
zero3nna
20

En términos generales, utilizamos el siguiente método de delegado para evitar el toque en todo tipo de UIControls:

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

Nota: NO haga esta verificación (verifique el tipo de clase Recognizer.view) el gestoRecognizerShouldBegin, no funcionará.

flypig
fuente
11

Aquí hay una versión de Swift 3.0 :

extension UIViewController: UIGestureRecognizerDelegate {

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

No olvide:

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

Marcelo Gracietti
fuente
6

Aquí hay una versión Swift:

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

No olvide:

  1. Haz que tu clase se ajuste a UIGestureRecognizerDelegate
  2. Haga su delegado objeto Tapper a uno mismo (por ejemplo: tapper.delegate = self)
rsc
fuente
5

En mi opinión, la mejor solución es usar el siguiente fragmento de código:

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