UITapGestureRecognizer натисніть на self.view, але ігноруйте підпрограми


95

Мені потрібно реалізувати функцію, яка буде викликати деякий код, коли я двічі натискаю на self.view (подання UIViewCotroller). Але проблема в тому, що у мене в цьому поданні є інший об'єкт користувацького інтерфейсу, і я не хочу приєднувати будь-який об'єкт розпізнавання до всіх них. Я знайшов цей метод нижче, як робити жест на моєму погляді, і я знаю, як він працює. Зараз я перебуваю перед гандикапом, який спосіб обрати для створення цього розпізнавача, ігноруючи підпрогляд. Будь-які ідеї? Дякую.

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

1
Я не впевнений, але чи намагалися ви встановити CancelTouchesInView на NO у розпізнавачі? так [doubleTap setCancelsTouchesInView: NO];
JDx,

Відповіді:


144

Вам слід прийняти UIGestureRecognizerDelegateпротокол всередині selfоб'єкта і викликати наведений нижче метод для перевірки подання. Усередині цього методу перевірте свій погляд touch.viewі поверніть відповідний bool (Так / Ні). Щось на зразок цього:

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

Редагувати: Будь ласка, також перевірте відповідь @ Ian!

Стрімкий 5

// MARK: UIGestureRecognizerDelegate methods, You need to set the delegate of the recognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
     if touch.view?.isDescendant(of: tableView) == true {
        return false
     }
     return true
}

ще одне запитання. У мене є кілька кнопок на моєму погляді, які повинні працювати. як я можу встановити для них якийсь пріоритет?
Матросов Олександр

якщо у ваших кнопок є власні розпізнавачі жестів (ймовірно), копайтеся в їх внутрішніх органах і хапайте їх, і читайте документи, -[UIGestureRecognizer requireGestureRecognizerToFail:]але це може працювати, не модифікуючи їх
bshirley

шукав це годинами! Дякую! ;)
MasterRazer

Якщо ваш ifтест не вдається, ваша реалізація не повертає BOOL; для належного стилю поверніть ТАК після блоку if (за допомогою { }) або у elseгілці. Подяка, проте, врятувала мене трохи читати.
RobP

2
Погляд є нащадком самого себе, тому це не працює ... (загальний підхід нормальний, див.
Іншу

107

Інший підхід полягає у простому порівнянні, якщо вигляд дотику є видом жестів, оскільки нащадки не пройдуть умови. Гарний, простий однокласник:

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

2
Для мене це працює краще, ніж прийнята відповідь. Набагато лаконічніше.
Suman Roy

1
@Ian цей метод не викликається при натисканні на подання.
Praburaj

1
Чудова стисла відповідь!
scurioni

Прийнята відповідь навіть не спрацювала, але це спрацювало для мене бездоганно.
Shalin Shah

@Prabu з можливістю розповсюдження, оскільки делегат розпізнавача жестів повинен бути встановлений раніше
Kubba,

23

А для варіанту Свіфт:

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

Корисно знати, isDescendantOfViewповертає Booleanзначення, яке вказує, чи є одержувач підвидом даного подання чи ідентичним цьому виду.


1
Не забудьте встановити UIGestureRecognizerDelegate у своєму класі!
Fox5150

4
Замість того, щоб робити таке твердження if, ви не можете просто повернути це? return! touch.view.isDescendant (of: gestureRecognizer.view) Крім того, новий синтаксис в швидкому 3 ^^
EdwardSanchez

12

У Swift 5 та iOS 12 UIGestureRecognizerDelegateє метод, який називається gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)має таку декларацію:

Запитайте делегата, чи повинен розпізнавач жестів отримати предмет, що представляє дотик.

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

Повний код нижче показує можливу реалізацію для gestureRecognizer(_:shouldReceive:). За допомогою цього коду натискання на підпрогляд 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's 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.


10

Повне швидке рішення (делегат повинен бути реалізований І встановлений для розпізнавачів):

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
        }
    }
}

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
  }
}

4

Ясний швидкий шлях

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

Ніколи не викликається в iOS 13, принаймні за допомогою swipeGestureRecognizer.
Chewie The Chorkie

Чим це відрізняється від відповіді Яна?
sweetfa

3

Зверніть увагу, що API gestureRecognizer змінено на:

gestureRecognizer (_: shouldReceive :)

Зверніть особливу увагу на індикатор підкреслення (пропуску) для зовнішньої мітки першого параметра.

Використовуючи багато прикладів, наведених вище, я не отримав події. Нижче наведено приклад, який працює для поточних версій 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
}

2

Плюс наведені вище рішення, не забудьте перевірити User Interaction Enabledсвій підгляд.

введіть тут опис зображення


2

Мені довелося запобігти жесту на погляд дитини. Єдине, що спрацювало, це дозволити та зберегти перший вигляд та запобігти жестам у всіх наступних поданнях:

   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
     }

1

Свіфт 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
}

0

Якщо ви не хочете, щоб ваш розпізнавач подвійного натискання конфліктував з вашими кнопками та / або іншими елементами керування, ви можете встановити selfяк UIGestureRecognizerDelegateта реалізувати:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.