Когда и как срабатывает Hit-Testing в UIKit?

«Когда и как срабатывает Hit-Testing в UIKit?» — вопрос из категории UIKit, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Hit-Testing — это процесс определения, какое представление (UIView) должно получить событие касания. Он запускается при каждом новом касании (touch event) системой UIApplication.

Алгоритм (метод hitTest(_:with:)): Процесс рекурсивный, начинается с корневого окна (UIWindow) и идет от верхних вью к нижним.

  1. Проверяются условия для текущей вью:
    • isUserInteractionEnabled == true
    • isHidden == false
    • alpha > 0.01
    • Точка касания находится внутри границ вью (bounds).
  2. Если условия не выполнены – вью и всё её поддерево игнорируются.
  3. Если условия выполнены, алгоритм проходит по массиву subviews в обратном порядке (от последней добавленной, которая визуально выше, к первой).
  4. Для каждой сабвью рекурсивно вызывается hitTest.
  5. Если сабвью возвращает не-nil (нашла подходящую вью), этот результат возвращается.
  6. Если ни одна сабвью не вернула результат, возвращается сама текущая вью.

Пример переопределения для расширения области касания:

class CustomButton: UIButton {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // 1. Проверяем стандартные условия
        guard self.isUserInteractionEnabled,
              !self.isHidden,
              self.alpha > 0.01 else {
            return nil
        }
        // 2. Расширяем область проверки на 20 пунктов во все стороны
        let expandedBounds = bounds.insetBy(dx: -20, dy: -20)

        // 3. Если точка в расширенной области, возвращаем self (эту кнопку)
        if expandedBounds.contains(point) {
            return self
        }
        // 4. Иначе передаем обработку стандартному алгоритму
        return super.hitTest(point, with: event)
    }
}

Итог: Hit-Testing гарантирует, что событие касания получает самый глубокий (верхний в иерархии) UIView, готовый его обработать.