Uigesturerecognizer блокирует подвид для обработки сенсорных событий


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

Я добавить UITableView как подвида a UIView. Элемент UIView реагирует на нажатие-и pinchGestureRecognizer, но при этом tableview перестает реагировать на эти два жеста (он все еще реагирует на пойло).

Я сделал это работать со следующим кодом, но это, очевидно, не хорошее решение, и я уверен, что есть лучший способ. Это помещается в 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 75

10 ответов:

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

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

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

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
r.cancelsTouchesInView = NO;
[agentPicker addGestureRecognizer:r];

Я показывал выпадающее представление, которое имело свой собственный tableview. В результате 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;
}

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

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

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

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

Если посмотреть.gestureRecognizers не содержит вашего gestureRecognizer, просто игнорировать его

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

проверка зрения.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
    }
}

здание на @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)
    }
}

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

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

Я также делал popover и вот как я это сделал

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) {
        sender.cancelsTouchesInView = false
    }
    else {
        delegate?.menuDimissed()
    }
}

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

вызов это метод 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.

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

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

}

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

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