В iOS, если на UIView добавлен UIGestureRecognizer, кто обрабатывает касание первым?

«В iOS, если на UIView добавлен UIGestureRecognizer, кто обрабатывает касание первым?» — вопрос из категории UIKit, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Первым событие касания получает UIGesture Recognizer, а не методы touchesBegan(_:with:) самого UIView. Это фундаментальный принцип архитектуры обработки событий в UIKit.

Порядок обработки событий

  1. Hit-Testing: Система определяет UIView, которая получила касание, с помощью hitTest(_:with:).
  2. Gesture Recognizer Priority: Для этой view система сначала предлагает событие всем attached UIGestureRecognizer.
  3. Распознавание: Если какой-либо gesture recognizer распознает жест (переходит в состояние .recognized), он может "захватить" поток событий.
  4. Delivery to View: События передаются методам touchesBegan view только если:
    • Все gesture recognizer не смогли распознать жест (состояние .failed).
    • Распознавателю установлено свойство cancelsTouchesInView = false.

Практический пример и контроль

class CustomView: UIView {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Этот код выполнится ПОСЛЕ gesture recognizer
        print("View touchesBegan")
    }
}

let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
// tapRecognizer получит и обработает событие ДО touchesBegan
myView.addGestureRecognizer(tapRecognizer)

// Для тонкого контроля можно использовать делегат:
tapRecognizer.delegate = self
// Метод делегата позволяет решить, должен ли recognizer получать события
// одновременно с view или блокировать их.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true // Разрешить одновременную работу нескольких распознавателей
}

Ключевые свойства:

  • cancelsTouchesInView (по умолчанию true): При распознавании отменяет доставку событий к view.
  • delaysTouchesBegan (по умолчанию false): Задерживает вызов touchesBegan у view до тех пор, пока recognizer не перейдет в состояние .failed.

Вывод: UIGestureRecognizer является приоритетным обработчиком в chain of responsibility для событий касания.