The init function calls a method loadLayers() so let’s see what that does.
When we call the method, first we create a circular path that will be used by the CAShapeLayers.
Since we want the foreground and pulse layers to have a gradient, I have applied the mask accordingly.
Lastly, I have added the UILabel in the center of the view that will be used to show the remaining time.
Animating the layersNow that we have setup all the layers, let’s look at how we can achieve the animations.
Below shows how can we animate the foreground layer.
We have declared a CABasicAnimation with keyPath 'strokeEnd' This means that we want to animate where the stroke will end.
The animation will go from beginning to the end (which is a circle as specified in our path earlier) over a specified duration.
Finally, we do not want our animation to be removed when done, so we set the isRemovedOnCompletion to false.
Last step is to add the animation to our foregroundLayer (CAShapeLayer)The pulsing layer animation is almost the same, except that we will be grouping 2 animations.
If you see the final outcome, our pulse layer scales outwards and also becomes less visible towards the end.
We will be declaring 2 CABasicAnimations with paths transform.
scale to adjust the size and opacity to adjust the visibility.
The layer will increase 20% in size and reduce to 0 opacity over the duration of the animation.
Once we have declared the 2 animations, we can use CAAnimationGroup to add the animations together.
Then to make it look better, and not linear like our foreground animation, we have set the timingFunction to easeInEaseOut this means our animation will begin slowly, accelerate through the middle of its duration, and then slow again before the end.
Lastly, we have set the duration to be 1 second, and to repeat the animation perpetually.
But don’t worry, when we detect that our foreground animation has stopped (which means that the remaining time is 0), we will remove the pulsing animation as well.
Finally, these are the rest of the methods found in our file.
HandleTimerTick reduces the remaining time by 0.
1 second and updates our UILabel using main thread (since it is an UI operation).
Our only public method is startCountdown which takes in duration and whether to animate the pulsation or not.
The animationDidStop delegate behaves the same way as our background gradient we did earlier.
Once our foreground layer finishes animating, we will remove all animations and invalidate our timer.
That is all we really need to get the same output!.Here are the 2 files if anyone is lost.
swift that does the background gradient animationOur custom progress bar classUsing the CountdownProgressBar classIn order to use our class, first I have created a UIView and placed it in the middle of the view.
I have then changed its class to our CountdownProgressBar class.
Finally, in my ViewController.
swift I start the animation as such:countdownProgressBar.
startCoundown(duration: 10, showPulse: true)Feel free to try out the pulsation animation or try disabling it.
Animations with and without the pulsing effectThat is it!.We have a done a really cool few animations that were not hard at all!.I have initially wrote my own custom countdown class for a project I was doing, and could not find many examples online.
Feel free to modify and use the code as you wish.
Where to go from hereThere is a lot more cool things you can do, with greater efficiency than UIView, using CALayers!Firstly, you can experiment with different values and colours to really understand how the code works.
For example, you can edit the background colours, adjust the radius of our pulsing effect, or play with the foreground layer colours.
These are just some simple examples how you can spice up your application.
It would be pretty cool to incorporate an animated background for your Login Page or creating your own custom loading bar.
Closing CommentsAs you may have already realised, I use this space to share some of the things I have learned on the job, and also to consolidate my own learning.
If you have any questions, feel free to ask me and I will do my best to provide an answer.