Туториалы
07 декабря 2022
Туториалы
07 декабря 2022
clipped() не оказывает влияния на проверку касаний

Модификатор 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:

Xcode preview displaying an orange square. A smaller square blue outline is centered in the orange square.

Теперь давайте добавим .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
    }
}


Когда вы запустите этот код, вы обнаружите, что кнопка не нажимается совсем. Это связано с тем, что (необрезанный) квадрат, несмотря на то, что он виден не полностью, закрывает(затеняет) эту кнопку и «крадет» все нажатия.

Xcode preview displaying a blue button and a small orange square. A larger dashed orange outline covers both the smaller square and the button.

Пунктирная линия обозначает область нажатия для оранжевого квадрата. Кнопку нельзя нажать, т.к. она закрыта обрезанным вью относительно к hit testing.

 

Решение: .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.


https://oleb.net/media/2022-11-24-clipped-hit-testing-demo.mp4


Итог

 

Модификатор clipped() не влияет на область проверки касания для обрезанного вью. То же самое верно и для clipShape(_:). Часто рекомендуется комбинировать эти модификаторы с .contentShape(Rectangle()), чтобы синхронизировать логику проверки касания с UI.

Оригинал статьи


Оцените статью
0
0
0
0
0

Чтобы добавить комментарий, авторизуйтесь
Войти
Безумова Виола
Пишет и переводит статьи для SwiftBook