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:
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):
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))
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.