I have the following shape drawn with some color & linewidth:
struct CustomShape: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: .zero)
path.addLine(to: CGPoint(x: rect.width, y: 0))
path.addLine(to: CGPoint(x: rect.width/2, y: rect.height))
path.closeSubpath()
return path
}
}
struct ContentView: View {
var body: some View {
ZStack {
CustomShape()
.stroke(Color.blue, lineWidth: 5)
.frame(width: 300, height: 200)
}
.ignoresSafeArea()
}
}
I’d like to draw 2 (possibly more) more rectangles inside the first one each with a different color & line width? Seems like I need the shape to conform to InsettableShape and I could then use stroke border but I’m not sure how.
2
Answers
I wasn’t familiar with
InsettableShape
, but with the help of this answer I learned how it could be used.The difficult part of this problem is working out the position for the triangle so that the edges of an inner triangle are equally spaced from the edges of an outer triangle. This requires a bit of trigonometry!
So here are two possible solutions, both using a
ZStack
to super-impose the shapes.Shape
that you provided. A y-offset is then applied to adjust the position of the nested shapes. For this solution, theZStack
usesalignment: .top
:Shape
has been converted to anInsettableShape
. The functionyTop
is derived from the functionyOffset
from above. TheZStack
uses default alignment this time.I would say, implementing the shape in this way is more complicated. However, the calling code (in other words,
ContentView
) is now simpler and I like the way the knowledge of the shape is all contained inside the shape itself.Both solutions give the same result:
This question is more about trigonometry than about coding. To create a good inset for the triangle, we first calculate the center of its inscribed circle. By converting the inset to a scale, we can then simply scale the shape around the center.