Распознаватель жестов и действия кнопок

99

У меня есть иерархия представлений, которая выглядит примерно так:

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

Я прикрепил распознаватели жестов к своему UIView (B). Проблема, с которой я столкнулся, заключается в том, что я не получаю никаких действий для кнопки Rounded Rect, которая находится внутри UIView (B). Распознаватель жестов singleTap фиксирует / отменяет событие Touch Up Inside для кнопки.

Как заставить его работать? Я думал, что иерархия цепочки респондентов будет гарантировать, что событию касания кнопки будет отдано предпочтение, и оно БУДЕТ запускаться! Что мне не хватает?

Вот какой-то связанный код:

#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;
    }
}
Мустафа
источник
Самый простой способ - установить cancelsTouchesInView = false
Bagusflyer

Ответы:

176

В методе shouldReceiveTouch вы должны добавить условие, которое вернет NO, если касание находится в кнопке.

Это из примера 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;
}

надеюсь, это поможет

редактировать

Как заметил Дэниел, вы должны соответствовать, чтобы UIGestureRecognizerDelegateэто работало.

Шани

Шаннога
источник
1
Ах !!! Да, я забыл об этом методе -gestureRecognizer: shouldReceiveTouch :. Спасибо, что указали мне правильное направление. Хотя это решает мою проблему, но я до сих пор не знаю, почему в этом случае не работает иерархия респондентов. Возможно, так и должно быть, но Apple этим не занимается.
Мустафа
2
Поведение UIGestureRecognizer четко описано как отдельное от «нормальной» цепочки респондентов. Это дизайнерское решение Apple.
Сильвен Г.
11
Не забудьте соответствовать протоколу UIGestureRecognizerDelegate и установить делегат UIGestureRecognizer для этого объекта.
Даниэль
2
Для более широкого использования рассмотрим какой-нибудь вариант тестовых положений Рамеша или мухи. В моем случае, например, я получил строку: if ( [touch.view isKindOfClass:[UIControl class]] || [[touch.view superview] isKindOfClass:[UITableViewCell class]] ) {... Обратите внимание, что ячейки tableview "касаются" в их contentView, который является членом частного класса, поэтому вместо этого мы проверяем класс суперпредставления.
Clozach
Спасибо за ответ! В моем случае я столкнулся с той же проблемой, но обнаружил ее только до тех пор, пока не протестировал фактическое устройство. Запуск приложения в симуляторе без использования UIGestureDelegate, как указано в этом ответе, работал должным образом, хотя и неправильно.
Луис Артола
51

У меня тоже была такая же проблема, потом я попробовал с

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

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

Теперь он работает отлично .........

Рамеш Чандран А
источник
4
я только пропустилmyGestureRecognizer.delegate = self;
zero3nna
20

Вообще говоря, мы используем нижеприведенный метод делегата, чтобы избежать касания во всех видах UIControls:

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

Примечание: НЕ выполняйте эту проверку (проверьте тип класса распознавателя .view) gestureRecognizerShouldBegin, это не сработает.

мухоловка
источник
11

Вот версия Swift 3.0 :

extension UIViewController: UIGestureRecognizerDelegate {

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

Не забудьте:

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

Марсело Грасетти
источник
6

Вот версия Swift:

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

Не забудьте:

  1. Заставьте ваш класс соответствовать UIGestureRecognizerDelegate
  2. Сделайте свой объект конусность делегат самостоятельно (например: tapper.delegate = self)
rsc
источник
5

На мой взгляд, лучшим решением является использование фрагмента кода ниже:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    CGPoint touchLocation = [touch locationInView:self.view];
    return !CGRectContainsPoint(self.menuButton.frame, touchLocation);
}
Одиноко
источник