I am using a SwiftUI Button
inside a HStack
as below.
import SwiftUI
struct ContentView: View {
@State var tapCount: Int = 0
var body: some View {
VStack(spacing: 10) {
HStack {
Image(systemName: "globe")
.frame(width: 48, height: 48)
.background(.green)
Text("Headline")
Spacer()
Text("See More")
.foregroundStyle(.white)
Button(action: {
tapCount += 1
}, label: {
Image(systemName: "x.circle.fill")
.frame(width: 48, height: 48)
.background(.yellow)
..contentShape(Rectangle()) // here
})
.contentShape(Rectangle()) // here
}
.background(.pink)
.padding(.horizontal, 16)
Text("Button Tap Count: (tapCount)")
.font(.system(size: 25))
}
}
}
And UI looks like below
And the button still tappable until the letter e
of See More
Text
why is that? and I tried to use .contentShape(Rectangle())
for the image and button but non of them fixed the issue. any idea how to fix this?
2
Answers
You are likely using a mouse pointer to click the button, and this is why you find this behaviour confusing. In reality, the user most likely uses their finger to tap the button, which covers an area, not just a point, and the iOS simulator simulates this. Hold the option key and you will see the area used for hit-testing. When I hover over the "e", that area clearly overlaps with the button,
In your real app, I assume "See More" is also tappable. If you add
onTapGesture
to that:Then when clicking on the "e", iOS correctly triggers the "See More" action, instead of the button’s action. This is because the finger’s area overlaps with "See More", more than it does with the button.
You can also reduce the tappable area with an insetted shape,
But people will likely find it much harder to tap the button this way.
You can also write your own insetted shape instead of
Rectangle
,Instead of
clipShape
modifier, you should be usingcontentShape
modifier on Button to define the shape of your button content. So, only that part will be tappable.Here is the corrected code.