UITapGestureRecognizer коснитесь self.view, но игнорирует subviews

Мне нужно реализовать функцию, которая будет вызывать некоторый код при двойном нажатии на self.view(вид UIViewCotroller). Но проблема в том, что у меня есть другой объект пользовательского интерфейса в этом представлении, и я не хочу прикреплять к нему всех объектов распознавателя. Я нашел этот метод ниже, как сделать жест на мой взгляд, и я знаю, как он работает. Прямо сейчас я перед гандикапом, который выбирает для создания этого распознавателя, игнорируя subview. Есть идеи? Спасибо.

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:doubleTap];

Ответ 1

Вы должны принять протокол UIGestureRecognizerDelegate внутри объекта self и вызвать метод ниже для проверки представления. Внутри этого метода проверьте свое мнение против touch.view и верните соответствующий bool (Да/Нет). Что-то вроде этого:

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

Изменить: Пожалуйста, также проверьте @Ian ответ!

Ответ 2

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

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}

Ответ 3

И для варианта Swift:

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

Полезно знать, isDescendantOfView возвращает значение Boolean, указывающее, является ли приемник подчиненным объектом данного представления или идентичным этому представлению.

Ответ 4

Полное быстрое решение (делегат должен быть реализован и установлен для распознавателя (-ов)):

class MyViewController: UIViewController UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onBaseTapOnly))
        doubleTapRecognizer.numberOfTapsRequired = 2
        doubleTapRecognizer.delegate = self
        self.view.addGestureRecognizer(doubleTapRecognizer)
    }

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

    func onBaseTapOnly(sender: UITapGestureRecognizer) {
        if sender.state == .Ended {
            //react to tap
        }
    }
}

Ответ 5

В Swift 5 и iOS 12 в UIGestureRecognizerDelegate есть метод с именем gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:) имеет следующее объявление:

Спросите делегата, должен ли распознаватель жестов получать объект, представляющий прикосновение.

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool

Полный код ниже показывает возможную реализацию для gestureRecognizer(_:shouldReceive:). С этим кодом нажатие на ViewController представления ViewController (включая imageView) не вызовет метод printHello(_:).

import UIKit

class ViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(printHello))
        tapGestureRecognizer.delegate = self
        view.addGestureRecognizer(tapGestureRecognizer)

        let imageView = UIImageView(image: UIImage(named: "icon")!)
        imageView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
        view.addSubview(imageView)

        // ⚠️ Enable user interaction for imageView so that it can participate to touch events.
        // Otherwise, taps on imageView will be forwarded to its superview and managed by it.
        imageView.isUserInteractionEnabled = true
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        // Prevent subviews of a specific view to send touch events to the view gesture recognizers.
        if let touchedView = touch.view, let gestureView = gestureRecognizer.view, touchedView.isDescendant(of: gestureView), touchedView !== gestureView {
            return false
        }
        return true
    }

    @objc func printHello(_ sender: UITapGestureRecognizer) {
        print("Hello")
    }

}

Альтернативной реализацией gestureRecognizer(_:shouldReceive:) может быть:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return gestureRecognizer.view === touch.view
}

Однако обратите внимание, что этот альтернативный код не проверяет, является ли touch.view gestureRecognizer.view.

Ответ 6

Вариант с использованием CGPoint, к которому вы прикасаетесь (SWIFT 4.0)

class MyViewController: UIViewController, UIGestureRecognizerDelegate {

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {

// Get the location in CGPoint
    let location = touch.location(in: nil)

// Check if location is inside the view to avoid
    if viewToAvoid.frame.contains(location) {
        return false
    }

    return true
  }
}

Ответ 7

Очистить Свифт путь

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}

Ответ 8

Обратите внимание, что gestureRecognizer API изменился на:

gestureRecognizer (_: shouldReceive:)

Обратите внимание на индикатор подчеркивания (skip) для первой метки внешнего параметра.

Используя многие из приведенных выше примеров, я не получал событие. Ниже приведен пример, который работает для текущих версий Swift (3+).

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    var shouldReceive = false
    if let clickedView = touch.view {
        if clickedView == self.view {
            shouldReceive = true;
        }
    }
    return shouldReceive
}

Ответ 9

Плюс вышеупомянутые решения, не забывайте проверять User Interaction Enabled в вашем подчиненном представлении.

enter image description here

Ответ 10

Свифт 4:

touch.view теперь является необязательным, поэтому основан на ответе @Antoine:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let touchedView = touch.view, touchedView.isDescendant(of: deductibleBackgroundView) {
        return false
    }
    return true
}

Ответ 11

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

   var gestureView: UIView? = nil

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if (gestureView == nil || gestureView == touch.view){
            gestureView = touch.view
            return true
        }
        return false
     }