Как UIView обрабатывает касания (тапы)?

Ответ

UIView обрабатывает касания благодаря наследованию от класса UIResponder. Основные механизмы:

  1. hitTest(_:with:) – определяет, какое view должно получить событие касания. Метод рекурсивно проходит по иерархии subviews, возвращая самое верхнее view, содержащее точку касания.
  2. point(inside:with:) – проверяет, находится ли точка внутри bounds view. Можно переопределить для нестандартной области отклика.
  3. Методы UIRespondertouchesBegan, touchesMoved, touchesEnded, touchesCancelled для прямой обработки жестов.

Пример прямой обработки:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    // Ваша логика обработки начала касания
}

Практический совет: Для большинства случаев удобнее использовать готовые UIGestureRecognizer (например, UITapGestureRecognizer), которые инкапсулируют сложную логику распознавания.

let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
myView.addGestureRecognizer(tapRecognizer)

Ответ 18+ 🔞

А, ну это же классика, блядь! Смотри, как оно работает, а то многие думают, что там магия какая-то, а на деле — простая механика, как утюг.

Вот есть у тебя UIView. Почему он на касания реагирует? Да потому что он, сука, наследник UIResponder! Это как базовая комплектация, из коробки. А внутри у него три главных кита, на которых всё держится, блядь.

Первый — это hitTest(_:with:). Это такой главный распределитель, менеджер по клиентам, ёпта. Система тыкает в экран пальцем, а этот метод начинает рекурсивно рыскать по всей иерархии твоих вьюх, сверху вниз, и ищет: а кто тут самый верхний и самый дохуя задний, в чьи границы эта точка попала? Его и возвращает. Он — избранный, ему и достанется событие.

Второй — point(inside:with:). Это уже проверка на месте: а точно ли точка в моих владениях? Внутри bounds? Его можно переписать, если тебе вдруг захотелось, чтобы кнопка откликалась не на всю себя, а только на какую-то хитрожопую область, например, круглую. Ну, или наоборот, чтобы отклик был больше, чем сама кнопка — бывает и такое, пиздец.

Ну и третий этаж — это прямые методы UIResponder: touchesBegan, touchesMoved и так далее. Это уже низкоуровневая работа, вручную. Подписался — и лови сырые данные о касаниях, как душе угодно.

Вот смотри, как это в коде выглядит, если делать по-старинке:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    // Ну вот, палец приложили. Делай тут что хочешь, рисуй, двигай, считай.
}

Но слушай сюда, самый важный практический совет, который сэкономит тебе овердохуища нервов: в 99% случаев не еби себе мозг этими touchesBegan!

Для этого, блядь, умные дядьки придумали UIGestureRecognizer. Это готовые распознаватели жестов: тап, пинч, свайп, пан — всё, что душе угодно. Они всю грязную работу по отслеживанию траектории, таймингов и прочей хуйни делают за тебя. Ты просто вешаешь его на вьюху и говоришь: «Когда пользователь сделает вот это, вызови мой метод». Красота, ёпта!

Вот так это выглядит, по-человечески:

let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
myView.addGestureRecognizer(tapRecognizer)

И всё! Сидишь потом в методе handleTap, попивая кофе, и обрабатываешь уже готовое, красиво упакованное событие. Не жизнь, а малина. Зачем городить велосипед, когда тебе дали готовый мерседес, а?