Rozpoznawanie gestów i działania przycisków

99

Mam hierarchię widoków, która wygląda mniej więcej tak:

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

Do mojego UIView (B) dołączyłem rozpoznawanie gestów. Problem, z którym się zmagam, polega na tym, że nie otrzymuję żadnych akcji dla przycisku Rounded Rect Button, który znajduje się wewnątrz UIView (B). Rozpoznawanie gestów za jednym dotknięciem przechwytuje / zastępuje zdarzenie Touch Up Inside przycisku.

Jak mogę to zrobić? Pomyślałem, że hierarchia łańcucha responderów zapewni, że zdarzenie dotknięcia przycisku będzie miało pierwszeństwo i zostanie wywołane! Czego mi brakuje?

Oto powiązany kod:

#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
źródło
Najprostszym sposobem jest ustawienie cancelsTouchesInView = false
Bagusflyer

Odpowiedzi:

176

W metodzie „shouldReceiveTouch” należy dodać warunek, który zwróci NIE, jeśli dotknięcie jest w przycisku.

To pochodzi z przykładu 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;
}

mam nadzieję, że to pomoże

Edytować

Jak zauważył Daniel, musisz się dostosować, aby UIGestureRecognizerDelegateto zadziałało.

shani

shannoga
źródło
1
Ach !!! Tak, zapomniałem o tej metodzie -gestureRecognizer: shouldReceiveTouch:. Dzięki za wskazanie mi właściwego kierunku. Chociaż to rozwiązuje mój problem, ale nadal nie wiem, dlaczego hierarchia respondentów nie działa w tym przypadku. Prawdopodobnie powinien, ale Apple nie obsługuje.
Mustafa
2
Zachowanie UIGestureRecognizer jest jasno opisane jako oddzielne od „normalnego” łańcucha odpowiedzi. To jest decyzja projektowa firmy Apple.
Sylvain G.
11
Pamiętaj, aby dostosować się do protokołu UIGestureRecognizerDelegate i ustawić delegata UIGestureRecognizer na ten obiekt.
Daniel
2
Dla szerszego zastosowania rozważ kilka wariantów klauzul testowych Ramesha lub flypiga. W moim przypadku, na przykład, skończyło się na linii: if ( [touch.view isKindOfClass:[UIControl class]] || [[touch.view superview] isKindOfClass:[UITableViewCell class]] ) {... Zauważ, że komórki tableview są „dotykane” w ich contentView, który jest członkiem prywatnej klasy, więc zamiast tego sprawdzamy klasę superview.
clozach
Dziękuję za Twoją odpowiedź! W moim przypadku napotkałem ten sam problem, ale odkryłem go tylko do momentu przetestowania w rzeczywistym urządzeniu. Uruchomienie aplikacji w symulatorze bez użycia UIGestureDelegate, jak wskazano w tej odpowiedzi, działało zgodnie z oczekiwaniami, choć niepoprawnie.
Luis Artola
51

Miałem też ten sam problem, potem próbowałem

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

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

Teraz działa doskonale .........

Ramesh Chandran A
źródło
4
przegapiłem tylkomyGestureRecognizer.delegate = self;
zero3nna
20

Ogólnie rzecz biorąc, używamy poniższej metody delegowania, aby uniknąć dotykania we wszelkiego rodzaju kontrolach UIC:

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

Uwaga: NIE WOLNO tego sprawdzać (sprawdź typ klasy rozpoznawania.view) gestRecognizerShouldBegin, nie zadziała.

flypig
źródło
11

Oto wersja Swift 3.0 :

extension UIViewController: UIGestureRecognizerDelegate {

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

Nie zapomnij:

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

Marcelo Gracietti
źródło
6

Oto wersja Swift:

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

Nie zapomnij:

  1. Spraw, aby Twoja klasa była zgodna UIGestureRecognizerDelegate
  2. Dokonanie obiektu odprowadzenia delegata do siebie (np tapper.delegate = self)
rsc
źródło
5

Moim zdaniem najlepszym rozwiązaniem jest użycie poniższego fragmentu kodu:

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