Ответ
Первым событие касания получает 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 для событий касания.
Ответ 18+ 🔞
Смотри, вот тебе на пальцах, как эта ебля с касаниями в UIKit работает, а то многие думают, что они в touchesBegan короли, а на деле — так, подстилка для настоящих боссов.
Кто главный на районе?
Первым на любое касание налетает не твой UIView с его touchesBegan(_:with:), а UIGestureRecognizer. Это, блядь, железное правило, архитектура такая, ёпта. Твоя вьюшка — это как честный работяга, а распознаватель жестов — это бригадир с бейсбольной битой. Сначала все события идут через него.
Как это всё происходит, по шагам:
- Hit-Testing (Кто под прицелом): Система тыкает пальцем в экран и спрашивает: «Эй,
hitTest(_:with:), кто тут самый верхний и прозрачный?» Находит твою вьюшку. - Приоритет Ганста-Распознавателей: Система смотрит на эту вьюшку и говорит: «О, тут прикреплены какие-то
UIGestureRecognizer. Ребята, вам что-нибудь надо?» И суёт событие сначала им. - Распознавание (Момент истины): Если какой-нибудь распознаватель хватается за событие и орет «Это моё! Я узнал тап/свайп/пинч!» (переходит в состояние
.recognized), то он может просто увести весь поток событий себе, как последняя мартышлюшка банан. - Доставка бедной вьюшке: События доползут до методов
touchesBeganтвоей вью только в двух случаях:- Все распознаватели посмотрели на касание, пожали плечами и сказали «Не, это не наш формат» (состояние
.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
// Вот этот метод — как договор между бандами. Разрешаем работать одновременно.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true // Да похуй, пусть все работают, лишь бы не дрались.
}
Главные рычаги давления (свойства):
cancelsTouchesInView(по дефолтуtrue): Если распознаватель сработал, он может отменить доставку событий вьюшке. Полный пиздец дляtouchesBegan.delaysTouchesBegan(по дефолтуfalse): Может специально задерживать вызовtouchesBeganу вью, пока сам не решит — получилось у него распознать жест или нет.
Вывод, ёпта: UIGestureRecognizer — это главный барин в этой иерархии, приоритетный обработчик в цепочке ответственности. Твоя вьюшка со своими touchesBegan — это так, запасной аэродром, на который садятся, только если все жесты промазали. Держи это в голове, и волнение ебать исчезнет.