SwiftUI are nice and fun to working with. You can read my previous article to get a bit knowledge about create circular control, it will make you easy to working with this article. This article also use Shape and Path, if you have worked with CoreGraphic before you will find it similar With CALayer, CAShapeLayer and UIBezierPath.
First of all, let split watch to small components for easy coding. A watch has these following parts:
- Circular Bezel
- Hour, Minus, Second hands.
- Number markers. from 1 to 12
As usual, open Xcode and create new project, don’t forget to choose SwiftUI, add new SwiftUI file and named it Watch.
Path is similar to BenzierPath, you can draw Oval, rectangle, line, arc using Path.
I choose Arc for now. Later on we can using Arc, Oval or Circle to make different watch face style.
Add a new struct and named it Arc conform to Shape protocol.
Xcode will guild you add missing path method to conform with Shape.
Path methods provided us rect which is the frame we are using to draw on it.
the Act function required center point, starting angle and ending angle, we need complete circle so let start with 0 and end with 2pi which 2 angle of circle in radiants.
Try our new Arc struct, nice we have a black circle, you can try replace stroke(lineWidth) by fill() and see what happened, also try different starting and ending angle to see what happened.
Create new Struct Named Tick, it also conform to Shape.
This tick shape simple draw a line front top center down to 5 points.
Try out our new Tick, also embed all of them inside ZStack and give frame to make all view inside Stack have same size.
Our next job is simple, just repeat 60 times and rotated it.
The angle for each tick just a circle divided by 60 (2pi/60)
For readable and maintainability, I create new view named Ticks for it.
This is What we have after repeat 60 time.
However at 5, 10, 15, 20… minutes we should make tick a bit longer.
Let change our Tick and Ticks View a bit to add this extra.
We have finished draw ticks, let move on hour Numbers.
This use the same technique with my previous article, feel free to read it.
Basically we create a VStack with Text and Spacer to make the text at alignment top and as tall as parent view. This trick helping us easy to rotate the texts and keep the same radius.
Just like Tick, we repeat it 12 times, because number in Watch show from 1 to 12, I use 1 -> 13 instead of 0 -> 12
Try it and We got beautiful numbers indicator.
Make a pad circle for our Watch Hands. This completed by helping of building circle.
Now the Hand, we draw a round rectangle from center to some where between center and top, depending on it is an hour, a minus or a second hand.
Create new Shape, named Hand, to adjust height we adding offset property.
Test minute hand, using frame width to adjust how wide it will.
Now repeat with hour and second hand
It hard to see because it overlapping each other. You can use debug view hierarchy view to verify it.
We are not finished yet, our watch look better if we add a red dot for second hand.
We are finished our Watch View but let it “run-able” by make hands tick real time.
Add a date State property wrapper. We will update this property each one second, Hour, Minus and Second hand will know this change and make adjustments.
Add a method to automatically update “date” variable.
Call this on view appear event of ZStack
The final thing is base on current date time, we calculate an angle for hour, minus and second hand
The code is it self explanatory, I don’t have anything else to tell you.
Now use angles above to make our hands rotation.
Congratulations, our watch now run as normal watch.
You can change Arc to Circle, mix with different color to get more watch face.
View more at: https://github.com/viettrungphan/SwiftUIGeometryPractice