Ответ
Первым событие касания получает UIGesture Recognizer, а не методы touchesBegan(_:with:) самого UIView. Это фундаментальный принцип архитектуры обработки событий в UIKit.
Порядок обработки событий
- Hit-Testing: Система определяет
UIView, которая получила касание, с помощьюhitTest(_:with:). - Gesture Recognizer Priority: Для этой view система сначала предлагает событие всем attached
UIGestureRecognizer. - Распознавание: Если какой-либо gesture recognizer распознает жест (переходит в состояние
.recognized), он может "захватить" поток событий. - Delivery to View: События передаются методам
touchesBeganview только если:- Все gesture recognizer не смогли распознать жест (состояние
.failed). - Распознавателю установлено свойство
cancelsTouchesInView = false.
- Все gesture recognizer не смогли распознать жест (состояние
Практический пример и контроль
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 для событий касания.