Ответ
Да, метод hitTest(_:with:) в UIView можно переопределить для кастомной логики определения, какое view должно получить касание.
Типичные сценарии использования:
- Расширение или сужение области касания.
- Игнорирование касаний для определенных subviews.
- Перенаправление событий.
Пример: расширение зоны касания
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()), так как они отрисовываются поверх друг друга. - Избегайте тяжелых вычислений внутри метода, так как он вызывается часто.
Ответ 18+ 🔞
О, слушай, смотри, вот эта штука hitTest(_:with:) в UIView — это же, блядь, прям магия какая-то, если её правильно переопределить. Можно творить, что хочешь, с этими касаниями, как будто ты шаман какой-то, а не разработчик.
Зачем это вообще, нахуй, нужно? Ну, например:
- Сделать кнопку, которую можно тыкать не только по самой кнопке, а ещё и за её пределы на 10 пикселей, потому что пальцы у пользователей, как сосиски, блядь.
- Игнорировать касания на каких-нибудь декоративных вьюхах, чтобы они не перехватывали события, как хитрая жопа.
- Перенаправить касание с одной вьюхи на другую, потому что логика у тебя там, ёпта, хитрая.
Вот смотри, как сделать кнопку с увеличенной зоной тыканья:
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. Теперь смотрим на детей (subviews), кто из них ближе к экрану
for subview in subviews.reversed() { // reversed() — потому что верхние вьюхи рисуются поверх
let convertedPoint = subview.convert(point, from: self)
if let hitView = subview.hitTest(convertedPoint, with: event) {
return hitView // Нашли, кто должен получить касание? Отлично, вот он.
}
}
// 4. Если дети отказались — тогда касание получаем мы сами
return self
}
}
Важные моменты, чтобы не обосраться:
- Эти базовые проверки в
guard— это святое, блядь. Без них можешь получить поведение, от которого волосы дыбом встанут. - Метод
point(inside:with:)— твой друг, если форма вьюхи не прямоугольная, а какая-нибудь ебаная скруглённая или вообще в виде звезды. subviews.reversed()— это важно, ёпта! Вьюхи, которые сверху (позже добавленные), должны проверяться первыми, логично же?- Не устраивай тут, блядь, вычисления вселенной внутри этого метода. Он вызывается постоянно, как сумасшедший, на каждый чих системы. Сделал его тяжёлым — и приложение будет тормозить, как черепаха в сиропе.