I would like to create a button out of my custom shapes, but it seems the contentShape is not taking effect when applied to the shape itself.
It only works when I add the modifier to the whole button. This seems contradictory to samples and wondering if there are side effects with this, or is there a more supported way to create buttons out of custom shapes?
struct ContentView: View {
let shape = Triangle()
var body: some View {
VStack {
Button {
print("Tapped: \(Date.now.timeIntervalSince1970)")
} label: {
shape
.stroke(Color.red, lineWidth: 10)
.frame(width: 200, height: 200)
//.contentShape(shape) <-- does not work!
}
.contentShape(shape) // <-- works but awkward
}
.padding(32)
}
}
struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
return path
}
}
>Solution :
I don’t see anything "awkward" here… by default button hit-test area is rectangular independently of Label (which is just presented label, button does not care about it).
If contentShape at that level confuses you, it is possible to create a modified styles and use it, like
Button {
// ... other code
}
.buttonStyle(TriangleButtonStyle())
// ...
struct TriangleButtonStyle: PrimitiveButtonStyle {
func makeBody(configuration: Configuration) -> some View {
Button(configuration)
.contentShape(Triangle())
}
}
or even make it generic on shape like TriangleButtonStyle<S: Shape> to allow any shape.
Tested with Xcode 13.4 / iOS 15.5
