SwiftUI Field Guide

Shapes

Most built-in shapes in SwiftUI accept their proposed size and fill the proposed size. Circle is the exception; it'll always report a square size. We can see the reported sizes in the sample below by enabling the border, which visualizes the shape's bounding box.

Code
Rectangle()    .fill(Color.accentColor)    /* .border(Color.red) */
Preview
RectangleRoundedRectangleEllipseCapsuleCircleUnevenRoundedRectangle
200

By default, a shape gets filled using the current foreground style. We can either use fill (as we did in the example above) or add a stroke to the shape. A stroke traces the shape, drawing half of the line width outside of the bounds (we can visualize the bounds by enabling the border).

Code
RoundedRectangle(cornerRadius: 10)    .stroke(Color.accentColor, lineWidth: 
)
/* .border(Color.red) */
Preview
RectangleRoundedRectangleEllipseCapsuleCircleUnevenRoundedRectangle
200

Insettable Shapes

Coming soon.

Custom Shapes

We can also define our own custom shapes. In essence, a shape is a wrapper around a Path — but it takes into account the proposed size. For example, we can write a shape that displays a checkmark sign. By using properties such as maxX instead of hard coded values, the path always fits exactly inside the proposed rectangle:

Code
struct Checkmark: Shape {    func path(in rect: CGRect) -> Path {        Path { p in            p.move(to: .init(x: rect.minX,                             y: rect.midY))            p.addLine(to: .init(x: rect.minX                                    +                                    rect.size.width                                        /                                        2,                                y: rect.maxY))            p.addLine(to: .init(x: rect.maxX,                                y: rect.minY))        }    }}

We can now use our shape as any other shape, for example by applying a stroke:

Code
Checkmark()    .stroke(Color.accentColor, lineWidth: 
)
/* .border(Color.red) */ .padding()
Preview
50
50

When we adjust the size of the frame above, the path of the shape fits exactly inside the rectangle (even when the stroke might draw outside). However, our custom shape does look distorted when it's not drawing within a square. We can enforce a square aspect ratio by returning a square size in the sizeThatFits method:

Code
struct Checkmark: Shape {    func path(in rect: CGRect) -> Path {            }        func sizeThatFits(_ proposal: ProposedViewSize) ->        CGSize {        let p =            proposal.replacingUnspecifiedDimensions()        let length = min(p.width, p.height)        return .init(width: length,                     height: length)    }}

Now our shape always renders with a square aspect ratio, regardless of the size we propose:

Code
Checkmark()    .stroke(Color.accentColor, lineWidth: 
)
/* .border(Color.red) */ .padding()
Preview
50
50

When we want more control over how the end points are drawn within a stroked shape, we can specify the line cap parameter using a custom stroke style. Likewise, we can specify the line join parameter to control the appearance of the line connections. The example below lets us tweak both parameters:

Code
Checkmark()    .stroke(Color.accentColor,            style: StrokeStyle(lineWidth: 
,
lineCap: .round, lineJoin: .round)) /* .border(Color.red) */ .padding(30)
Preview
MiterRoundBevel
ButtRoundSquare
100