Модификатор clipped() в SwiftUI обрезает вью до своих(модификатора) границ, скрывая все, что выходит за пределы этих границ. Но обратите внимание, что обрезание не влияет на проверку касаний (hit testing); обрезанный вью по-прежнему принимает тапы / клики за пределами видимой области.
Протестировано на iOS 16.1 и macOS 13.0.
Пример
У нас есть квадрат 300×300, который затем мы ограничиваем кадром 100×100. Также добавим границу вокруг внешней рамки, чтобы визуализировать вью:
Rectangle()
.fill(.orange.gradient)
.frame(width: 300, height: 300)
// Set view to 100×100 → renders out of bounds
.frame(width: 100, height: 100)
.border(.blue)
Вьюшки в SwiftUI по умолчанию не обрезают свой контент, поэтому весь квадрат размером 300×300 остается видимым. Обратите внимание на синюю рамку, обозначающую границы кадра 100×100:
Теперь давайте добавим .clipped(),
чтобы обрезать большой квадрат до кадра 100×100. Я также сделал квадрат
кликабельным и добавил кнопку:
VStack {
Button("You can't tap me!") {
buttonTapCount += 1
}
.buttonStyle(.borderedProminent)
Rectangle()
.fill(.orange.gradient)
.frame(width: 300, height: 300)
.frame(width: 100, height: 100)
.clipped()
.onTapGesture {
rectTapCount += 1
}
}
Когда вы запустите этот код, вы обнаружите, что кнопка не нажимается совсем. Это связано с тем, что (необрезанный) квадрат, несмотря на то, что он виден не полностью, закрывает(затеняет) эту кнопку и «крадет» все нажатия.
Решение: .contentShape()
Модификатор contentShape(_:) определяет область проверки
касания для вью. Добавляя .contentShape(Rectangle()) к кадру 100×100, мы ограничиваем проверку касания
этой областью, делая кнопку снова доступной для нажатия:
Rectangle()
.fill(.orange.gradient)
.frame(width: 300, height: 300)
.frame(width: 100, height: 100)
.contentShape(Rectangle())
.clipped()
Обратите внимание, что порядок .contentShape(Rectangle()) и .clipped() можно поменять местами. Важно то, что contentShape является (косвенным) родителем модификатора кадра 100×100, который определяет размер области проверки касания.
Видео
Я сделал короткое видео, демонстрирующее эффект:
Первоначально нажатия на кнопку или даже на окружающее пустое пространство регистрируются как нажатия на квадрат.
Верхний переключатель дает нам увидеть квадрат перед обрезкой. Тем самым показывая нам его область проверки касаний.
Второй переключатель добавляет .contentShape(Rectangle()) , ограничивая проверку попаданий видимой(оранжевой) областью. Теперь нажатие кнопки увеличивает счет нажатий на нее.
Полный код доступен на GitHub.
Итог
Модификатор clipped() не влияет на область проверки касания для обрезанного вью. То же самое верно и для clipShape(_:). Часто рекомендуется комбинировать эти модификаторы с .contentShape(Rectangle()), чтобы синхронизировать логику проверки касания с UI.