Как событие касания (touch event) доходит до обработчика в iOS?

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

Ответ

Обработка событий касания в iOS основана на паттерне Responder Chain (Цепочка ответчиков). Все объекты, способные обрабатывать события, являются подклассами UIResponder (UIApplication, UIView, UIViewController).

Последовательность обработки события:

  1. Hit-Testing: Система определяет, какой UIView находится под точкой касания. UIWindow вызывает метод hitTest(_:with:), который рекурсивно проходит по иерархии вью, начиная с корневой, чтобы найти самый глубокий (frontmost) сабвью, содержащий точку касания. Этот вью становится first responder для данного события.

  2. Передача по цепочке ответчиков (Responder Chain): Если first responder (обычно UIView) не обрабатывает событие, оно передается вверх по цепочке:

    • От UIView к его UIViewController (если есть).
    • От UIView к его супервью (superview).
    • От корневого вью контроллера к UIWindow.
    • От UIWindow к UIApplication.
    • От UIApplication к AppDelegate (если он является UIResponder и реализует соответствующие методы).
  3. Обработка: Событие останавливается на первом UIResponder в цепочке, который реализует и вызывает соответствующий метод (например, touchesBegan(_:with:)).

Практический пример:

class CustomView: UIView {
    // 1. View может обработать событие сам
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("Touch handled in CustomView")
        // Если не вызвать super, событие не пойдет дальше по цепочке
        // super.touchesBegan(touches, with: event)
    }
}

class ViewController: UIViewController {
    // 2. Если View не обработал, событие дойдет до его ViewController
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("Touch handled in ViewController")
        super.touchesBegan(touches, with: event) // Может передать дальше, например, родительскому VC
    }
}

Ключевые методы UIResponder для касаний: touchesBegan, touchesMoved, touchesEnded, touchesCancelled.

Управление цепочкой: Можно вручную передать событие следующему ответчику, вызвав super-реализацию метода, или переназначить first responder с помощью методов becomeFirstResponder() / resignFirstResponder().