Tapping on Stack Views

A quick tip on using the contentShape view modifier to solve a SwiftUI gesture problem that often catches me out.

Container Views and Gestures

Here’s a typical scenario with nested vertical stack views:

VStack {
  Text("Action")
    .font(.title)
  Spacer()
    
  VStack(spacing: 8) {
    Label("Attention", systemImage: "alarm")
      .font(.headline)
    Text("Something went wrong!")
  }
  .padding()

  Spacer()
}
.frame(maxHeight: 250)
.padding()
.border(.black, width: 4)
.onTapGesture(count: 2) {
    print("doubleTap")
}

This is how that looks:

White rectangle with action label at top and attention, something went wrong text in the middle.

I attached the double tap gesture to the outer stack view. I want to be able to tap anywhere inside that outer stack view and have the double-tap gesture fire.

The problem is that container views like VStack or HStack don’t automatically act as hit targets for gestures. In my example, the double-tap gesture only fires when I tap on one of the text or label views (coloured yellow below):

Three text labels with yellow background in a white rectangle

Nothing happens if I double-tap on the surrounding space not covered by a content view. Adding a background view to my inner stack view does improve the situation:

  VStack(spacing: 8) {
  }
  .padding()
  .background(.yellow.gradient,
              in: RoundedRectangle(cornerRadius: 8))

Yellow rectangle showing Attention, something went wrong in larger which rectangle with Action title at the top

Now a double-tap anywhere on the yellow background works but there’s a better way to tell SwiftUI that I want the whole outer stack view to act as a tap target.

Content Shape for Hit Testing

The contentShape view modifier defines the content shape used for hit testing gestures. Applying this to my outer VStack using a rectangle shape makes the full bounds of the stack view a hit target for taps:

VStack {
}
.contentShape(Rectangle())
.onTapGesture(count: 2) {
    print("doubleTap")
}

A double-tap anywhere in the outer stack view now triggers my tap gesture action.