SwiftUI is a modern, simple and powerful framework that allows us to build user interfaces across all Apple platforms. By using it we can create complex views and animations in a declarative way.
In this article, we are going to build a completely custom iOS App that will help you to learn more about SwiftUI.
The App that we are going to build is an iOS version of the popular breathe app from apple watch. The design concept was created by the wonderful designer Daniel Korpai.
I was really inspired by his concept and decided to create a complete app for iOS using SwiftUI.
In this article, I would like to explain my complete process and challenges for implementing the UI for this app. This is an excellent opportunity to dive deeper into the benefits of using SwiftUI and learn more about creating custom views, animation and transitions using the SwiftUI.
Creating the Flower View
Let us start by creating the most core component of the Breathe App, the flower view. This component is really popular from the Breathe App for watchOS.
Flower Component is used for both selecting the number of minutes that we want to breathe, and also for presenting a beautiful animation while the user is breathing.
Drawing the Flower View
Basically from the technical perspective, our flower component is a combination of multiple circles that have a common point, the common point being the center of the flower view.
The most important thing for drawing these circles is a way to define the center points of them. From there than we could easily find the offsets of the circles regarding the x and y-axis of the container view.
If we draw a right triangle inside of the circle, with the hypotenuse being the radius of the circle and a vertex that corresponds to the center of the circle we can then use trigonometry to find the coordinates of this vertex.
Currently, we have as a known parameter the radius of the circle that we are drawing so we know the length of our hypotenuse, but we still miss the length of the sides which is a must for finding the vertex that corresponds to the circle center
Let’s zoom to our triangle to clarify more and explain how we calculate each of the parameters that are needed.
Calculating our angle alpha is really simple, all we have to do is divide circle circumference (360 degrees) to the number of petals that we want to draw for the flower.
The h side is also known parameter since it’s basically the radius of the circle and the point A is the center of the container for our flower component.
We need to find point B as it will represent the center of the circle.
If we had the length a and b we could simply use the distance between points to find that.
Luckily we can use trigonometry formulas of the right triangle here.
From the above we can see that we can easily find the length of the side a and b, now we can use that knowledge in order to find our B point.
Now that we found our point B we have the center point of our first circle.
All that we have to do now is simply iterate for the number of desired circles and draw them by applying the above formulas.
Perfect, let us see it in action
You can find the whole code for the flower view here.
Animating petal additions
Our Flower Drawing looks really nice but there is one thing missing, we want to animate smoothly the petal additions and not jump from one circle to another. That is exactly what we are going to do in this section.
Animations in SwiftUI are managed by recreating the same view over and over again until the view reaches the final state.
What is really nice about animations in SwiftUI is that the system handles all the interruptions and drawing on changes in the view state, once you have given the system the right values of interpolation for different states.
Since our goal is to animate the number of flower petals, We can use animatable data on the the AnimatableModifier object.
We need to tell SwiftUI how to draw the view multiple times, by using immediate values between for example one circle and two circles. Basically, what should the system draw on the state of example 1.2 circles?
Luckily all we have to do is use double value for the petal count and add a slight improvement to our iteration, and then SwiftUI will automatically handle the rest.
Great, let’s see this in action:
Adding the Breathe animation
What makes the Breathe App really feel more alive is the beautiful animations that it has. The Flower animation that is ongoing while the user is breathing pushes the user experience to a new level.
In this section, we will now focus on breaking down and recreating this animation using SwiftUI.
From the technical perspective for animating the breathing of the flower, we need to be able to animate the scale of the flower and not only the sides of it. Afterward, we could create an infinite animation that animates scaling up and down until the breathing time is over.
What is really nice about the animatable data property is that we can use animatable pairs for animating multiple parameters. In our case the circle count and the scale of the individual circles.
We can see from the image above how the flower will look at different stages of scaling down.
We have only one problem here if we only animate the flower scaling down and up, relative to the container size, the animation won't match the given animation from above.
This is because our flower petals need to scale not linearly to the container size. We need to slightly increase our circle size while the container size is decreasing. So that the animation looks visually more appealing because it somehow feels that are the circles are meeting in the middle.
After applying the above improvement we can now take a deep breath while enjoying the beautiful flower animation.
Creating The Ring View
Our next challenge would be to create the beautiful Ring view of the Breathe App. This component is used for giving visual feedback to the users when the daily goal has been achieved, and also for creating a dashboard for them so they can track their progress of mindful breathing.
Drawing the ring
Our ring component consists of two ring views, one simple gray ring on the background and a more complex gradient ring on the front.
For creating the foreground ring, first, we need to define our gradient. For this, we will use the Angular Gradient Provided By SwiftUI.
Afterward, we want to mask our gradient view by using a Ring Shape. So we can have a gradient ring view.
The only problem here is that ring color does not look consistent at the ring tip. This is because of the clipping point of our angular gradient.
Rotating the gradient to avoid the point where it breaks would not work for the full circle. Therefore we try to solve this issue by overlaying our ring with a small circle view.
Of course, we will change our ring head color to be the same as our gradient end color.
After we add our background circle, our ring component is completed and looks pretty great.
Animating the ring view
Our next challenge is to animate the ring view to a full state, this will be useful when our users finish their breathing exercise and we want to give them visual feedback about it.
We can use animatable data for animating the end angle of the control. The important thing is that we need to animate the rotation of all three of our views to achieve the desired effect. The ring shape, the gradient itself and also the ring head.
We take care of that when we draw our views by calculating End Angle in all of the views when we draw them.
We have now completed the ring animation and we can use that inside of our App.
Applying View Transitions
In order to create a better user experience for our App, we want to create a smooth view transition after the breathing exercise is finished. We will focus on this section on final details like adding this view transition.
Transitions in SwiftUI enable you to define the transition animation between the insertion and removal state of a view.
Transitions in SwiftUI come with really useful features. Firstly the insertion and removal don't have to be the same animation for insertion and removal, they can be two completely different animations based on our needs.
.transition(.asymmetric(insertion: .opacity, removal: .opacity))
Secondly, you can combine multiple modifiers for adding transitions to the view.
Lastly, we can add custom parameters
Basically, that is all the knowledge that we need to start creating our final state transition. We will combine multiple transitions on different views and we will use a slight delay between the animations so they appear smoothly after each other.
Nice and easy, now that we have all the details finished let us see it in action.
In this article by using SwiftUI, we have built together step by step user interfaces for a fully custom iOS App. We have seen how things like animation, transitions and drawing custom views work by using SwiftUI.
You also had the chance to discover my complete thinking processes behind creating a custom app starting from a design concept to simplifying the problem and then providing a custom solution.
If you enjoyed this article make sure to clap to show your support. Follow me to view many more iOS articles, and get notified when the new ones are out.
Subscribe to my newsletter for downloading source code for the full project, I will send it to you once it’s fully available.
Also, my full SwiftUI course is coming out soon, you may want to subscribe to not miss out on that as well.