Как в UIKit обрабатывается пользовательское касание (тап)?

Ответ

Обработка строится на Responder Chain (цепочке ответчиков). При касании система создает объект UIEvent с UITouch и передает его по цепочке:

  1. Hit-Testing: UIWindow находит самую глубокую вью, получившую касание, с помощью метода hitTest(_:with:).
  2. Цепочка ответчиков: Событие передается от этой вью (UIView) вверх по иерархии ответчиков (UIResponder): UIView -> UIViewController -> UIWindow -> UIApplication.

Способы обработки:

  • Переопределение методов UIResponder: Прямая обработка в кастомной вью.
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // Логика обработки начала касания
    }
  • Использование UIControl (например, UIButton):
    • Через addTarget:
      button.addTarget(self, action: #selector(handleTap), for: .touchUpInside)
    • Через @IBAction в Interface Builder.

Почему цепочка? Это обеспечивает гибкость: если вью не обработала событие, его может перехватить родитель или контроллер.

Ответ 18+ 🔞

А, ну это ж классика, блядь! Касания в iOS — это как разборки в подъезде: кто первый попал, тот и разбирается, а если не смог — эстафета передаётся выше, пока кто-нибудь не возьмёт на себя эту ебучую ответственность.

Вот смотри, вся эта движуха строится на Responder Chain, то есть цепочке ответчиков. Когда палец шлёпается по экрану, система, сука, создаёт объект UIEvent с UITouch внутри и начинает его пинать по этой самой цепочке:

  1. Hit-Testing (Поиск жертвы): Сначала UIWindow методом hitTest(_:with:) ищет самую глубокую, самую занюханную вьюху, на которую приземлилось касание. Типа "эй, ты, вон тот самый нижний UIView под пальцем — выходи, разбираться будешь!"
  2. Цепочка ответчиков (Передача стрелы): А дальше начинается цирк. Событие, как горячая картошка, передаётся наверх по иерархии этих самых UIResponder: от вьюхи (UIView) -> к её контроллеру (UIViewController) -> потом к окну (UIWindow) -> и в самом конце, если все проебали, к самому приложению (UIApplication). "На, мужик, лови! — А я не могу! — На, контроллер, лови! — А я нихуя! — Ну тогда ты, UIApplication, расхлёбывай!"

Как можно это всё обработать, спросишь? Да вариантов — овердохуища!

  • Переопределение методов UIResponder (Дикий запад): Берёшь свою кастомную вьюху и прямо в ней пишешь логику. По-пацански, без посредников.
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // Ну всё, твои пальцы на мне, делаю что хочу
    }
  • Использование UIControl (Цивилизованно): Берёшь готовую штуку, типа UIButton.
    • Через addTarget — программистский способ, чтоб мозги не выносило:
      button.addTarget(self, action: #selector(handleTap), for: .touchUpInside)
    • Через @IBAction в Interface Builder — для тех, кто любит потыкать в сториборд.

А нахуя вообще эта цепочка нужна? А затем, чтоб была гибкость, ёпта! Если одна вьюха сдулась и не смогла обработать событие (типа "ой, я не для этого"), его может подхватить её родитель или контроллер. Как в той поговорке: один проебал — другой нашёл. Вся система от этого только выигрывает, потому что не нужно в каждой вьюхе городить огород на все случаи жизни. Гениально и просто, как хуй с горы!