Warning: Undefined array key "pathway" in /home/clients/160b20c93964292618c158d21ce27bf5/sites/tech.org-services.ch/wp-content/themes/Newspaper/functions.php on line 543
Monday, December 5, 2022
HomeiOS Developmentclipped() doesn’t have an effect on hit testing – Ole Begemann

clipped() doesn’t have an effect on hit testing – Ole Begemann


The clipped() modifier in SwiftUI clips a view to its bounds, hiding any out-of-bounds content material. However word that clipping doesn’t have an effect on hit testing; the clipped view can nonetheless obtain faucets/clicks exterior the seen space.

I examined this on iOS 16.1 and macOS 13.0.

Instance

Right here’s a 300×300 sq., which we then constrain to a 100×100 body. I additionally added a border across the outer body to visualise the views:

Rectangle()
  .fill(.orange.gradient)
  .body(width: 300, peak: 300)
  // Set view to 100×100 → renders out of bounds
  .body(width: 100, peak: 100)
  .border(.blue)

SwiftUI views don’t clip their content material by default, therefore the total 300×300 sq. stays seen. Discover the blue border that signifies the 100×100 outer body:

Now let’s add .clipped() to clip the massive sq. to the 100×100 body. I additionally made the sq. tappable and added a button:

VStack {
  Button("You'll be able to't faucet me!") {
    buttonTapCount += 1
  }
  .buttonStyle(.borderedProminent)

  Rectangle()
    .fill(.orange.gradient)
    .body(width: 300, peak: 300)
    .body(width: 100, peak: 100)
    .clipped()
    .onTapGesture {
      rectTapCount += 1
    }
}

If you run this code, you’ll uncover that the button isn’t tappable in any respect. It is because the (unclipped) sq., regardless of not being totally seen, obscures the button and “steals” all faucets.


Xcode preview displaying a blue button and a small orange square. A larger, partially transparent orange square covers both the smaller square and the button.
The partially clear area signifies the hit space of the orange sq.. The button isn’t tappable as a result of it’s lined by the clipped view with respect to hit testing.

The repair: .contentShape()

The contentShape(_:) modifier defines the hit testing space for a view. By including .contentShape(Rectangle()) to the 100×100 body, we restrict hit testing to that space, making the button tappable once more:

  Rectangle()
    .fill(.orange.gradient)
    .body(width: 300, peak: 300)
    .body(width: 100, peak: 100)
    .contentShape(Rectangle())
    .clipped()

Word that the order of .contentShape(Rectangle()) and .clipped() might be swapped. The vital factor is that contentShape is an (oblique) guardian of the 100×100 body modifier that defines the scale of the hit testing space.

Video demo

I made a brief video that demonstrates the impact:

  • Initially, faucets on the button, and even on the encompassing whitespace, register as faucets on the sq..
  • The highest change toggles show of the sq. earlier than clipping. This illustrates its hit testing space.
  • The second change provides .contentShape(Rectangle()) to restrict hit testing to the seen space. Now tapping the button increments the button’s faucet rely.

The complete code for this demo is accessible on GitHub.

Abstract

The clipped() modifier doesn’t have an effect on the clipped view’s hit testing area. The identical is true for clipShape(_:). It’s typically a good suggestion to mix these modifiers with .contentShape(Rectangle()) to carry the hit testing logic in sync with the UI.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments