Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Hit Test — определение UI элемента, получившего touch
Что такое Hit Test
Hit test (проверка попадания) — это процесс, при котором iOS определяет, какой UIView получит touch event в ответ на касание пользователя.
Когда пользователь касается экрана, система проходит через иерархию view и ищет самый глубокий view, который находится в этой точке.
Как работает Hit Test
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 1. Проверяем, находится ли точка внутри нашего view
guard self.bounds.contains(point) else {
return nil // Touch не попал в этот view
}
// 2. Проходим через все сублейеры
for subview in subviews.reversed() { // Обратный порядок!
let convertedPoint = convert(point, to: subview)
if let hitView = subview.hitTest(convertedPoint, with: event) {
return hitView // Нашли самый глубокий view
}
}
// 3. Если no subviews hit, возвращаем сам себя
return self
}
Пример: порядок обхода
Window
├── ViewController.view
│ ├── Button A (добавлен первый)
│ ├── Button B (добавлен вторым)
│ └── Button C (добавлен третьим)
Если касание попадает на перекрытие Button B и Button C, то Button C получит touch (был добавлен последним, на вершине).
Hit test проходит в обратном порядке (от последнего к первому).
Переопределение Hit Test
Кейс 1: Расширить область попадания
class LargeButton: UIButton {
let hitAreaInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let expandedBounds = bounds.inset(by: hitAreaInsets)
guard expandedBounds.contains(point) else {
return nil
}
return super.hitTest(point, with: event)
}
}
Кейс 2: Отключить интерактивность
class NonInteractiveView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Всегда пропускаем touches
return nil
}
}
Кейс 3: Перенаправить touch другому view
class PassthroughView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Пропускаем себя, передаём супервью
return super.hitTest(point, with: event)
}
}
Практический пример: кастомный overlay
class ModalOverlay: UIView {
weak var contentView: UIView?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Разрешаем touches только для контентного view
guard let contentView = contentView else {
return nil // Overlay не интерактивен
}
let convertedPoint = convert(point, to: contentView)
if contentView.bounds.contains(convertedPoint) {
return contentView.hitTest(convertedPoint, with: event)
}
// Touch вне contentView — пропускаем
return nil
}
}
Связь с Point Inside
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// Проверяет, находится ли точка внутри view
// Используется внутри hitTest
return bounds.contains(point)
}
UIControl и Hit Test
class CustomButton: UIControl {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if isEnabled && !isHidden && alpha > 0.01 {
return super.hitTest(point, with: event)
}
return nil // Disabled кнопка не получает touches
}
}
Отладка Hit Test
Способ 1: Логирование
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
print("Hit test для \(self) в точке \(point)")
return super.hitTest(point, with: event)
}
Способ 2: Визуальная отладка в Xcode
- Debug → View Hierarchy
- Показывает структуру view
- Подсвечивает элементы при hover
Ключевые свойства
let view = UIView()
// Влияют на hit test
view.isHidden = true // Скрытый view не получит touch
view.alpha = 0 // Прозрачный view не получит touch
view.isUserInteractionEnabled = false // Отключить touches
view.bounds = .zero // Пустой view не получит touch
Порядок Z-axis (layer order)
// Порядок добавления определяет Z порядок
view.addSubview(buttonA) // z = 0
view.addSubview(buttonB) // z = 1
view.addSubview(buttonC) // z = 2 (вверху)
// Если все три перекрываются, touch пойдёт в buttonC
Ключевые правила
✅ Hit test проходит в обратном порядке (от вершины вниз) ✅ Переопределяй hitTest когда нужна кастомная логика ✅ Помни про isUserInteractionEnabled, alpha, isHidden ✅ Расширяй области попадания для small buttons ✅ Используй hitTest для сложных жестов ❌ Не забывай вызывать super.hitTest ❌ Не игнорируй isHidden — это влияет на hit test
Hit test — критический механизм для правильной обработки пользовательских касаний. Правильное понимание этого процесса важно для создания responsive интерфейсов.