UIGestureRecognizer блокирует подпредставление для обработки сенсорных событий - PullRequest
80 голосов
/ 07 марта 2011

Я пытаюсь выяснить, как это сделать правильно . Я попытался изобразить ситуацию: enter image description here

Я добавляю UITableView как подпредставление UIView. UIView отвечает на нажатие и pinchGestureRecognizer, но при этом табличное представление перестает реагировать на эти два жеста (оно все еще реагирует на пролистывание).

Я заставил его работать со следующим кодом, но это, очевидно, не очень хорошее решение, и я уверен, что есть лучший способ. Это помещено в UIView (супер-обзор):

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if([super hitTest:point withEvent:event] == self) {
        for (id gesture in self.gestureRecognizers) {
            [gesture setEnabled:YES];
        }
        return self;
    }
    for (id gesture in self.gestureRecognizers) {
        [gesture setEnabled:NO];
    }
    return [self.subviews lastObject];
}

Ответы [ 10 ]

180 голосов
/ 04 мая 2011

У меня была очень похожая проблема, и я нашел решение в этом вопросе .Таким образом, установите себя в качестве делегата для UIGestureRecognizer, а затем проверьте целевое представление, прежде чем разрешить распознавателю обрабатывать касание.Соответствующий метод делегата:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
105 голосов
/ 20 июля 2012

Блокировка сенсорных событий для подпредставлений является поведением по умолчанию.Вы можете изменить это поведение:

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
r.cancelsTouchesInView = NO;
[agentPicker addGestureRecognizer:r];
5 голосов
/ 24 июля 2013

Я отображал раскрывающееся подпредставление, которое имело свой собственный вид таблицы.В результате touch.view иногда возвращает классы, такие как UITableViewCell.Я должен был пройти через суперкласс (ы), чтобы убедиться, что это подкласс, я думал, что это было:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    UIView *view = touch.view;
    while (view.class != UIView.class) {
        // Check if superclass is of type dropdown
        if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own
            NSLog(@"Is of type dropdown; returning NO");
            return NO;
        } else {
            view = view.superview;
        }
    }

    return YES;
}
4 голосов
/ 27 сентября 2017

Опираясь на @Pin Shih Wang ответ . Мы игнорируем все касания, кроме тех, которые находятся в представлении, содержащем распознаватель жестов касания. Все касания перенаправляются в иерархию представления как обычно, как мы установили tapGestureRecognizer.cancelsTouchesInView = false. Вот код в Swift3 / 4:

func ensureBackgroundTapDismissesKeyboard() {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
    tapGestureRecognizer.cancelsTouchesInView = false
    self.view.addGestureRecognizer(tapGestureRecognizer)
}

@objc func handleTap(recognizer: UIGestureRecognizer) {
    let location = recognizer.location(in: self.view)
    let hitTestView = self.view.hitTest(location, with: UIEvent())
    if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) {
        // I dismiss the keyboard on a tap on the scroll view
        // REPLACE with own logic
        self.view.endEditing(true)
    }
}
4 голосов
/ 08 марта 2011

Одной из возможностей является создание подкласса вашего распознавателя жестов (если вы этого еще не сделали) и переопределение -touchesBegan:withEvent: таким образом, чтобы оно определяло, начиналось ли каждое касание в исключенном подпредставлении, и вызывает -ignoreTouch:forEvent: для этого касания, если оно имело место.

Очевидно, вам также необходимо добавить свойство для отслеживания исключенного подпредставления или, что еще лучше, массива исключенных подпредставлений.

2 голосов
/ 14 апреля 2014

Можно обойтись без наследования какого-либо класса.

Вы можете проверить gestRecognizer в селекторе обратного вызова жеста

, если view.gestureRecognizers не содержит ваш gestRecognizer, просто проигнорируйте его

например

- (void)viewDidLoad
{
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self     action:@selector(handleSingleTap:)];
    singleTapGesture.numberOfTapsRequired = 1;
}

проверьте view.gestureRecognizers здесь

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer
{
    UIEvent *event = [[UIEvent alloc] init];
    CGPoint location = [gestureRecognizer locationInView:self.view];

    //check actually view you hit via hitTest
    UIView *view = [self.view hitTest:location withEvent:event];

    if ([view.gestureRecognizers containsObject:gestureRecognizer]) {
        //your UIView
        //do something
    }
    else {
        //your UITableView or some thing else...
        //ignore
    }
}
1 голос
/ 12 декабря 2016

реализует делегат для всех распознавателей parentView и помещает метод gestRecognizer в делегат, который отвечает за одновременный запуск распознавателей:

func gestureRecognizer(UIGestureRecognizer,       shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool {
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) {
    return true
    } else {
    return false
}

}

U может использоватьМетоды сбоя, если вы хотите, чтобы запускались дочерние элементы, но не родительские распознаватели:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate

1 голос
/ 18 сентября 2014

Я создал подкласс UIGestureRecognizer, предназначенный для блокировки всех распознавателей жестов, прикрепленных к суперпредставлениям определенного представления.

Это часть моего проекта WEPopover. Вы можете найти это здесь .

0 голосов
/ 29 октября 2015

Я тоже делал поповер, и вот как я это сделал

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) {
        sender.cancelsTouchesInView = false
    }
    else {
        delegate?.menuDimissed()
    }
}
0 голосов
/ 01 декабря 2012

Вы можете выключить и включить .... в моем коде я сделал что-то вроде этого, так как мне нужно было выключить его, когда клавиатура не показывалась, вы можете применить его к вашей ситуации:

вызовите это viewdidload и т.д.:

NSNotificationCenter    *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil];

затем создайте два метода:

-(void) notifyShowKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=true;  // turn the gesture on
}

-(void) notifyHideKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=false;  //turn the gesture off so it wont consume the touch event
}

Это отключает кран. Мне пришлось превратить tap в переменную экземпляра и выпустить его в dealloc.

...