How to Create a Complex Loading Animation in Swift

Learn how to create a complex loading animation in Swift with this step-by-step tutorial. By Satraj.

Leave a rating/review
Save for later

With more than 1.4 million apps in the iOS App Store today, it’s a real challenge to make your app stand out. You have a very small window of opportunity to capture the attention of your users before your app ends up in the big black hole of obscurity.

There’s no better place to start wowing your users than at the loading screen of your app, where you can add a delightful animation that serves as a precursor to your on-boarding or authentication workflow.

In this tutorial you will learn how to make such an animation. You’ll learn how to build it up piece-by-piece, utilising advanced techniques to create a fluid and captivating animation.

Getting Started

Download the starter project for this tutorial here, save it to a convenient location and open it in Xcode.

Open HolderView.swift. In this UIView subclass, you will add and animate the following sublayers (found in the Layers subgroup) as shown in the animation above:

  • OvalLayer.swift: This is the first layer, which expands from zero size and then wobbles for a short period of time.
  • TriangleLayer.swift: This next layer appears while the OvalLayer is wobbling. When this view rotates, OvalLayer contracts back to zero size leaving just the TriangleLayer visible.
  • RectangleLayer.swift: This layer serves as a visual container of sorts for the TriangleLayer.
  • ArcLayer.swift: This layer fills the RectangleLayer with an animation effect that’s very similar to a glass being filled with water.

Open OvalLayer.swift; the starter project already contains the code to initialize this layer and all the Bezier paths you’ll use in your animations. You’ll see that expand(), wobble() and contract() are all empty; you’ll populate those methods as you work through the tutorial. All the other *Layer files are structured in a similar fashion.

Note: If you want to learn more about Bezier paths, then check out our Modern Core Graphics with Swift tutorial series.

Finally, open ViewController.swift and take a look at addHolderView(); this method adds an instance of HolderView as a subview to the center of the view controller’s view. This view will house all the animations. The view controller just needs to put it on the screen, and the view will take care of the actual animation code.

The animateLabel() function is a delegate callback provided by the HolderView class that you will fill in as you complete the animation sequence. addButton() simply adds a button to the view so that you can tap and restart the animation.

Build and run your app; you should see an empty white screen. An empty canvas — the perfect thing on which to start creating your new animations! :]

By the end of this tutorial, your app will look like this:


So without further ado, let’s get started!

Adding The Oval

The animation starts with a red oval that expands into view from the centre of the screen and then wobbles around a bit.

Open HolderView.swift and declare the following constant near the top of the HolderView class:

let ovalLayer = OvalLayer()

Now add the following function to the bottom of the class:

func addOval() {

This first adds the OvalLayer instance you created above as a sublayer to the view’s layer, then calls expand(), which is one of the stubbed-out functions you need to fill in.

Go to OvalLayer.swift and add the following code to expand():

func expand() {
  var expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
  expandAnimation.fromValue = ovalPathSmall.CGPath
  expandAnimation.toValue = ovalPathLarge.CGPath
  expandAnimation.duration = animationDuration
  expandAnimation.fillMode = kCAFillModeForwards
  expandAnimation.removedOnCompletion = false
  addAnimation(expandAnimation, forKey: nil)

This function creates an instance of CABasicAnimation that changes the oval’s path from ovalPathSmall to ovalPathLarge. The starter project provides both of these Bezier paths for you. Setting removedOnCompletion to false and fillMode to KCAFillModeForwards on the animation lets the oval retain its new path once the animation has finished.

Finally, open ViewController.swift and add the following line to addHolderView() just below view.addSubview(holderView):


This calls addOval to kickstart the animation after it has been added to the view controller’s view.

Build and run your app; your animation should now look like this:


Wobbling The Oval

With your oval now expanding into view, the next step is to put some bounce in its step and make it wobble.

Open HolderView.swift and add the following function to the bottom of the class:

func wobbleOval() {

This calls the stubbed-out method wobble() in OvalLayer.

Now open OvalLayer.swift and add the following code to wobble():

func wobble() {
  // 1
  var wobbleAnimation1: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation1.fromValue = ovalPathLarge.CGPath
  wobbleAnimation1.toValue = ovalPathSquishVertical.CGPath
  wobbleAnimation1.beginTime = 0.0
  wobbleAnimation1.duration = animationDuration
  // 2
  var wobbleAnimation2: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation2.fromValue = ovalPathSquishVertical.CGPath
  wobbleAnimation2.toValue = ovalPathSquishHorizontal.CGPath
  wobbleAnimation2.beginTime = wobbleAnimation1.beginTime + wobbleAnimation1.duration
  wobbleAnimation2.duration = animationDuration
  // 3
  var wobbleAnimation3: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation3.fromValue = ovalPathSquishHorizontal.CGPath
  wobbleAnimation3.toValue = ovalPathSquishVertical.CGPath
  wobbleAnimation3.beginTime = wobbleAnimation2.beginTime + wobbleAnimation2.duration
  wobbleAnimation3.duration = animationDuration
  // 4
  var wobbleAnimation4: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation4.fromValue = ovalPathSquishVertical.CGPath
  wobbleAnimation4.toValue = ovalPathLarge.CGPath
  wobbleAnimation4.beginTime = wobbleAnimation3.beginTime + wobbleAnimation3.duration
  wobbleAnimation4.duration = animationDuration
  // 5
  var wobbleAnimationGroup: CAAnimationGroup = CAAnimationGroup()
  wobbleAnimationGroup.animations = [wobbleAnimation1, wobbleAnimation2, wobbleAnimation3, 
  wobbleAnimationGroup.duration = wobbleAnimation4.beginTime + wobbleAnimation4.duration
  wobbleAnimationGroup.repeatCount = 2
  addAnimation(wobbleAnimationGroup, forKey: nil)

That’s a lot of code, but it breaks down nicely. Here’s what’s going on:

  1. Animate from the large path down to being squished vertically.
  2. Change from a vertical squish to squished both horizontally and vertically.
  3. Swap back to vertical squish.
  4. Finish the animation, ending back at the large path.
  5. Combine all of your animations into a CAAnimationGroup and add this group animation to your OvalLayout.

The beginTime of each subsequent animation is the sum of the beginTime of the previous animation and its duration. You repeat the animation group twice to give the wobble a slightly elongated feel.

Even though you now have all the code required to produce the wobble animation, you aren’t calling your new animation yet.

Go back to HolderView.swift and add the following line to the end of addOval():

NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "wobbleOval", 
                                       userInfo: nil, repeats: false) 

Here you create a timer that calls wobbleOval() right after the OvalLayer has finished expanding.

Build and run your app; check out your new animation:


It’s very subtle, but that’s an important factor of a truly delightful animation. You don’t need things to be flying all over the screen!





Over 300 content creators. Join our team.