Как переопределить hitTest(_:with:) в UIKit?

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

Ответ

Да, метод hitTest(_:with:) в UIView можно переопределить для кастомной логики определения, какое view должно получить касание.

Типичные сценарии использования:

  1. Расширение или сужение области касания.
  2. Игнорирование касаний для определенных subviews.
  3. Перенаправление событий.

Пример: расширение зоны касания

class TouchExtendedView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // 1. Стандартные проверки (обязательно)
        guard self.isUserInteractionEnabled,
              !self.isHidden,
              self.alpha > 0.01,
              self.point(inside: point, with: event) else {
            return nil
        }

        // 2. Расширяем область проверки на 10 пунктов
        let expandedBounds = self.bounds.insetBy(dx: -10, dy: -10)
        guard expandedBounds.contains(point) else {
            return nil
        }

        // 3. Рекурсивная проверка дочерних view (стандартное поведение)
        for subview in subviews.reversed() {
            let convertedPoint = subview.convert(point, from: self)
            if let hitView = subview.hitTest(convertedPoint, with: event) {
                return hitView
            }
        }

        // 4. Если дочерние view не обработали, возвращаем self
        return self
    }
}

Best practices:

  • Всегда выполняйте базовые проверки (isUserInteractionEnabled, isHidden, alpha).
  • Для сложной геометрии используйте point(inside:with:).
  • Проверяйте дочерние view в обратном порядке (reversed()), так как они отрисовываются поверх друг друга.
  • Избегайте тяжелых вычислений внутри метода, так как он вызывается часто.