← Назад к вопросам

Как проверить, попала ли точка нажатия на конкретную View?

1.8 Middle🔥 161 комментариев
#UIKit и верстка

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Проверка попадания точки нажатия в границы View

В iOS существует несколько способов проверки, попадает ли точка касания в конкретную UIView. Выбор метода зависит от контекста и целей проверки.

Основные методы проверки

1. Метод point(inside:with:)

Это стандартный метод UIView, который система вызывает автоматически во время обработки цепочки касаний (hit-testing). Однако его можно использовать и для ручной проверки.

let view = UIView(frame: CGRect(x: inp_x, y: inp_y, width: inp_width, height: inp_height))
let testPoint = CGPoint(x: inp_x_test, y: inp_y_test)

// Проверка, находится ли точка внутри bounds view
let isPointInside = view.point(inside: testPoint, with: nil)
print("Точка внутри view: \(isPointInside)")

Важно: Метод использует систему координат bounds view (относительную), поэтому если вы проверяете точку из системы координат супервью (например, из touch.location(in:)), ее нужно предварительно конвертировать:

// Конвертация точки из системы координат супервью в систему координат целевой view
let pointInViewCoordinates = superview.convert(testPoint, to: view)
let isInside = view.point(inside: pointInViewCoordinates, with: nil)

2. Метод frame.contains(_:)

Наиболее простой и часто используемый способ для ручной проверки в коде. Работает с системой координат frame (абсолютной относительно супервью).

let viewFrame = view.frame
let isPointInsideFrame = viewFrame.contains(testPoint)

if isPointInsideFrame {
    print("Точка попадает в frame view")
} else {
    print("Точка вне frame view")
}

Ключевое отличие от point(inside:with:): When comparing frame.contains() and point(inside:), the first one uses the superview's coordinate system, while the second one uses the view's own coordinate system (bounds). This is critical when the view is transformed (rotated, scaled).

3. Метод bounds.contains(_:)

Проверка в относительной системе координат самой view.

// Эта проверка будет корректна, только если testPoint уже в координатах bounds view
let isPointInsideBounds = view.bounds.contains(testPoint)

Практические сценарии использования

В обработчике касаний (touchesBegan)

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else { return }
    
    // Получаем точку касания в системе координат self.view
    let touchLocation = touch.location(in: self.view)
    
    // Проверяем несколько дочерних view
    for subview in self.view.subviews {
        if subview.frame.contains(touchLocation) {
            print("Касание попало в \(subview)")
            // Дополнительная логика обработки
        }
    }
}

С учетом transform view

Если view имеет transform (поворот, масштаб), frame.contains() может дать некорректный результат, так как frame в этом случае представляет ограничивающий прямоугольник в системе супервью. Для точной проверки нужно использовать hit-testing:

// Создаем view с поворотом
let rotatedView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
rotatedView.transform = CGAffineTransform(rotationAngle: .pi / 4)

let testPoint = CGPoint(x: 100, y:...")

// НЕКОРРЕКТНО: frame может не соответствовать видимой области
let usingFrame = rotatedView.frame.contains(testPoint)

// КОРРЕКТНО: конвертируем и используем point(inside:)
let pointInRotatedView = self.view.convert(testPoint, to: rotatedView)
let usingHitTest = rotatedView.point(inside: pointInRotatedView, with: nil)

Проверка попадания в нестандартную область

Для сложных форм (круглые кнопки, многоугольники) стандартных методов недостаточно. Реализуйте кастомную логику в point(inside:with:):

class CircleView: UIView {
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        // Проверяем, находится ли точка внутри круга (радиус = половина ширины)
        let center = CGPoint(x: bounds.width / 2, y: bounds.height / 2)
        let distance = sqrt(pow(point.x - center.x, 2) + pow(point.y - center.y, 2))
        return distance <= bounds.width / 2
    }
}

Сравнение методов

МетодСистема координатУчитывает transformТипичное использование
frame.contains()Супервью (absolute)НетПростые проверки в коде, когда view не трансформирована
bounds.contains()Сама view (relative)Да (через bounds)Когда точка уже в координатах view
point(inside:with:)Сама view (relative)ДаHit-testing, кастомные формы, общая система проверки

Рекомендации по использованию

  1. Для большинства случаев вручную написанного кода используйте frame.contains(), предварительно убедившись, что точка и frame находятся в одной системе координат.
  2. При работе с трансформированными view всегда используйте цепочку convert(_:to:) + point(inside:with:).
  3. В кастомных view с нестандартной областью касания переопределяйте point(inside:with:).
  4. Производительность: Все методы достаточно быстры, но при проверке множества view в цикле стоит учитывать оптимизацию (например, предварительная проверка по крупным зонам).
// Пример полной проверки с конвертацией координат
func isTouch(_ touchPoint: CGPoint, inTargetView targetView: UIView) -> Bool {
    // Конвертируем точку из системы координат self.view в систему координат targetView
    let pointInTargetCoordinates = self.view.convert(touchPoint, to: targetView)
    
    // Используем point(inside:) для корректной работы с transforms
    return targetView.point(inside: pointInTargetCoordinates, with: nil)
}

Этот подход гарантирует корректную работу во всех случаях, включая вложенные иерархии view и различные трансформации.

Как проверить, попала ли точка нажатия на конкретную View? | PrepBro