Reconhecedor de gestos e ações de botão

99

Eu tenho uma hierarquia de visualização parecida com esta:

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

Anexei reconhecedor (es) de gestos ao meu UIView (B). O problema que estou enfrentando é que não consigo nenhuma ação para o Botão Reto Arredondado que está dentro do UIView (B). O reconhecedor de gestos singleTap captura / substitui o evento Touch Up Inside do botão.

Como posso fazer funcionar? Eu pensei que a hierarquia da cadeia de resposta garantirá que o evento de toque do botão terá preferência e será acionado! O que estou perdendo?

Aqui estão alguns códigos relacionados:

#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
fonte
A maneira mais simples é definir cancelsTouchesInView = false
Bagusflyer

Respostas:

176

No método "shouldReceiveTouch" você deve adicionar uma condição que retornará NÃO se o toque for no botão.

Isso é do exemplo SimpleGestureRecognizers da apple .

- (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 ajude

Editar

Como Daniel observou, você deve estar em conformidade UIGestureRecognizerDelegatepara que funcione.

Shani

Shannoga
fonte
1
Ah !!! Sim, esqueci-me deste método -gestureRecognizer: shouldReceiveTouch :. Obrigado por me apontar na direção certa. Embora isso resolva meu problema, ainda não sei por que a hierarquia de respondentes não está funcionando neste caso. Provavelmente deveria, mas não está sendo tratado pela Apple.
Mustafa
2
O comportamento do UIGestureRecognizer é claramente descrito como separado da cadeia de resposta "normal". Esta é uma decisão de design da Apple.
Sylvain G.
11
Lembre-se de estar em conformidade com o protocolo UIGestureRecognizerDelegate e definir o delegado do UIGestureRecognizer para esse objeto.
Daniel
2
Para um uso mais amplo, considere algumas variantes das cláusulas de teste de Ramesh ou flypig. No meu caso, por exemplo, acabei com a linha: if ( [touch.view isKindOfClass:[UIControl class]] || [[touch.view superview] isKindOfClass:[UITableViewCell class]] ) {... Observe que as células tableview são "tocadas" em seu contentView, que é membro de uma classe privada, então lá verificamos a classe do superview.
clozach
Obrigado pela resposta! No meu caso, encontrei o mesmo problema, mas só o descobri até testar no dispositivo real. Executar o aplicativo no simulador sem usar UIGestureDelegate conforme indicado nesta resposta funcionou como esperado, embora incorretamente.
Luis Artola,
51

Eu também tive o mesmo problema, então tentei com

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

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

Agora está funcionando perfeitamente .........

Ramesh Chandran A
fonte
4
Eu só perdi omyGestureRecognizer.delegate = self;
zero3nna
20

De um modo geral, usamos o método delegado abaixo para evitar o toque em todos os tipos de UIControls:

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

Nota: NÃO faça esta verificação (verifique o tipo de classe reconhecizer.view) o gestoRecognizerShouldBegin, ele não funcionará.

porco-espinho
fonte
11

Aqui está uma versão do 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ão se esqueça de:

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

Marcelo Gracietti
fonte
6

Aqui está uma versão Swift:

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

Não se esqueça de:

  1. Faça sua classe em conformidade com UIGestureRecognizerDelegate
  2. Faça seu objeto seringueiro delegar a si mesmo (por exemplo tapper.delegate = self:)
rsc
fonte
5

A melhor solução, em minha opinião, é usar o snippet de código abaixo:

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