SwiftUI Plain Button Background Tap
When using a `SwiftUI` `Button` with `.buttonStyle(.plain)`, the background of the button may not react to tap gestures. One may run into this situation when using a more complex composition of UI elements as a button label.
To make the background of the button respond to user interactions, use the modifier `.contentShape()`. Usually `.contentShape(Rectangle())` is a fitting choice.
In the sample code the upper button only responds to taps on the images with the arrows. The lower button also responds to taps on the background, because the modifier `.contentShape` is used.
import SwiftUI
struct SampleSpacedButtonView: View {
@State private var isExpanded: Bool = false
var body: some View {
VStack(spacing: 16) {
Text("Spaced Button")
.font(.largeTitle)
VStack(spacing: 16) {
Text("Background is not clickable:")
.padding(.top, 32)
VStack {
Button(action: {
isExpanded.toggle()
}, label: {
VStack {
HStack {
Image(systemName: "arrow.down.forward.square.fill")
if isExpanded {
Spacer()
}
Image(systemName: "arrow.down.backward.square.fill")
}
if isExpanded {
Spacer()
}
HStack {
Image(systemName: "arrow.up.forward.app.fill")
if isExpanded {
Spacer()
}
Image(systemName: "arrow.up.backward.square.fill")
}
}
.foregroundStyle(Color.accentColor)
.frame(width: 200, height: 100)
})
.buttonStyle(.plain)
}
.background {
Color.accentColor.opacity(0.1)
}
.border(Color.green)
Text("Background is clickable:")
.padding(.top, 32)
VStack {
Button(action: {
withAnimation {
isExpanded.toggle()
}
}, label: {
VStack {
HStack {
Image(systemName: "arrow.down.forward.square.fill")
if isExpanded {
Spacer()
}
Image(systemName: "arrow.down.backward.square.fill")
}
if isExpanded {
Spacer()
}
HStack {
Image(systemName: "arrow.up.forward.app.fill")
if isExpanded {
Spacer()
}
Image(systemName: "arrow.up.backward.square.fill")
}
}
.foregroundStyle(Color.accentColor)
.frame(width: 200, height: 100)
.contentShape(Rectangle())
})
.buttonStyle(.plain)
}
.background {
Color.accentColor.opacity(0.1)
}
.border(Color.green)
Spacer()
}
.frame(maxWidth: .infinity)
.background {
Color(white: 1.0)
.ignoresSafeArea(edges: .top)
}
}
.frame(maxWidth: .infinity)
.background {
Color(white: 0.95)
.ignoresSafeArea(edges: .top)
}
}
}
#Preview {
SampleSpacedButtonView()
}