You have to draw an arc or a semicircle in SwiftUI. The most obvious way to do that is to use a Path. In this blog post you can see how to do that, but it is really not rocket science.
Turns out there is an even quicker and easier way to draw a fraction of a circle. SwiftUI’s Shapes have a method called trim(from:to:) that allows you the specify what fraction of a Shape’s path you want to draw. If you want to draw a semi circle you could call that method like this:
As you can see the path of a Circle shape starts at the right center (or 03:00 hours on a clock). Because the value that you pass as from parameter has to be smaller than the value for the to parameter, you cannot draw a semicircle from top to bottom like this because that would mean that you would have to set from to 0.75 and to to 0.25. That is not allowed.
With a little trick you can still achieve that. You simply draw a semicircle from 0.0 to 0.5 and then rotate the shape by -90 degrees. IMHO still simpler that using a Path for this.
You can use this trim method on all SwiftUI’s built-in Shapes. For example if you want to draw a right triangle with SwiftUI you can do this:
Rectangle()
.trim(from: 0.0, to: 0.5)
.fill(Color("pdMagenta"))
.frame(width: 120, height: 80)
Of course you could easily draw a Path for this, but I think the trim method is a pretty cool tool that you can use for drawing semicircles, quarter-circles or triangles.
Now we need to let SwiftUI know what kind of arc we want to draw. As a human I would describe the arc as starting at 90 degrees and ending at 180 degrees, because we are usually assuming that 0 degrees is at the top of a circle.
SwiftUI begs to differ. In SwiftUI 0 degrees means straight to the right. In other words:
Human 90 degrees == SwiftUI 0 degrees
Because of that we need to draw and arc from 0 degrees to 90 degrees. (I add a gray background, so we can see the frame of the arc)
Seems pretty straightforward, but the result is a not what I would expect:
This is where things get wild. According to Apple’s docs SwiftUI (and UIKit) use a flipped coordinate system.
To explain what that means let’s have look at these two illustrations:
In a Cartesian coordinate system (that we humans are used to) increasing values on the Y-axis go up to the top. Resulting in the “clockwise” that we are used to.
In SwiftUI’s flipped coordinate system increasing values on the Y-axis are going down to the bottom of the canvas. And that results in a reversed “clockwise” direction. IMHO that is really counterintuitive, but that’s the way it is 🤷♂️ This is an excerpt from the Apple docs: “In a flipped coordinate system … specifying a clockwise arc results in a counterclockwise arc”
In other words: Clocks run backwards in a SwiftUI context.
To get the arc we want we have to toggle the clockwise parameter in our custom ArcShape: