В каком порядке срабатывают события при нажатии на кнопку и почему
Что проверяют
Знаете ли вы, как касание доходит от системы до кнопки: hit-testing, доставка касаний, события UIControl и цепочка ответчиков. Следом обычно спрашивают, почему действие висит на .touchUpInside, а не на .touchDown, и кто победит, если на родителе есть UITapGestureRecognizer.
Как отвечать
Опишите путь по шагам:
- доставка события: система передаёт касание в
UIApplication, та черезsendEvent(_:)отдаёт его окну; - hit-testing:
UIWindowвызываетhitTest(_:with:), который идёт вниз по иерархии, проверяетpoint(inside:with:)и перебирает сабвью начиная с верхнего. Скрытые вью, вью сisUserInteractionEnabled = falseи почти прозрачные (alpha меньше 0.01) пропускаются. Самая глубокая подходящая вью, здесь кнопка, становится получателем; - распознаватели жестов: касания сначала видят gesture recognizers на этой вью и её предках, и только потом сама вью;
- касания: кнопка получает
touchesBegan,touchesMoved,touchesEndedилиtouchesCancelled, аUIControlпереводит их вbeginTracking,continueTracking,endTracking; - события контрола:
.touchDownпри касании (кнопка переходит вisHighlighted),.touchDragExitи.touchDragEnterпри уходе пальца за границу и возврате, в конце.touchUpInsideили.touchUpOutside. При прерывании приходит.touchCancel; - действие:
sendAction(_:to:for:)уходит вUIApplication, а приnilв target обработчик ищется вверх по цепочке ответчиков. С iOS 14 то же делают черезUIActionиaddAction(_:for:).
Почему так: пока палец не поднят, система не знает, тап это, скролл или пользователь передумал. Срабатывание на отпускании позволяет отменить нажатие, уведя палец с кнопки.
Где спотыкаются
Путают направления: hit-testing идёт от окна вниз к самой глубокой вью, а цепочка ответчиков поднимается от неё вверх к контроллеру и окну.
Не знают, что стандартное действие контрола имеет приоритет над одиночным тапом распознавателя на родителе, и лечат «не срабатывающую кнопку» костылями.
Забывают про UIScrollView: из-за delaysContentTouches кнопка внутри скролла подсвечивается с задержкой, и кандидат ищет баг в своём коде.