Как обрабатывается нажатие в UIView
Что проверяют
Понимаете ли вы путь касания от системы до кода: hit-testing, цепочку ответчиков и место распознавателей жестов в ней. Ответ «добавить UITapGestureRecognizer» засчитают как начало, но следом спросят, почему кнопка за пределами родителя не нажимается.
Как отвечать
Касание проходит несколько шагов:
- доставка: система создаёт
UIEventсUITouch,UIApplicationпередаёт его окну черезsendEvent(_:); - hit-testing: окно вызывает
hitTest(_:with:), который рекурсивно обходит подвиды от верхнего к нижнему, то есть с конца массиваsubviews, и для каждого зовётpoint(inside:with:). Вью пропускается, если у неё выключенisUserInteractionEnabled, стоитisHiddenилиalphaменьше 0.01. Самая глубокая подходящая вью становится hit-test view; - распознаватели жестов: касания первыми получают распознаватели на этой вью и её предках. Если жест распознан, при включённом по умолчанию
cancelsTouchesInViewвью получаетtouchesCancelled; - цепочка ответчиков: вью получает
touchesBegan,touchesMoved,touchesEnded. Если она не обрабатывает событие и вызываетsuper, оно уходит к следующемуUIResponder: superview, контроллер, окно, приложение; UIControl: кнопки работают через target-action (addTarget(_:action:for:)с.touchUpInside), а начиная с iOS 14 удобнееaddActionсUIAction.
На практике для простой вью берут UITapGestureRecognizer, свои контролы наследуют от UIControl, а переопределение touchesBegan оставляют для рисования и нестандартной работы с касаниями. Область нажатия маленькой кнопки расширяют переопределением point(inside:with:).
Где спотыкаются
Не знают, что UILabel и UIImageView по умолчанию не принимают касания. Жест добавлен, а ничего не происходит, пока не включить isUserInteractionEnabled.
Делают подвид, вылезающий за границы родителя, и удивляются, что его край не нажимается. Hit-testing не заходит в подвиды, если точка вне bounds родителя, даже при выключенном clipsToBounds.
Путают порядок и думают, что жест срабатывает после touchesEnded. На деле распознаватель получает касания первым и может отменить их доставку во вью.