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
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Filling In The Container

With your container now in place, the next phase of the animation is to fill it up. The effect you’re looking for is that of water filling up a glass. This is a great visual effect and sets things up for a big…splash! :]

Open HolderView.swift and add the following constant just below the two RectangleLayer properties:

let arcLayer = ArcLayer()

Now add the following code to the end of drawBlueAnimatedRectangle():

NSTimer.scheduledTimerWithTimeInterval(0.40, target: self, selector: "drawArc", 
                                       userInfo: nil, repeats: false)

This creates a timer to call drawArc() once the blue RectangleLayer finishes drawing.

Add the following function to the end of the class:

func drawArc() {
  layer.addSublayer(arcLayer)
  arcLayer.animate()
}

This adds the instance of ArcLayer created above to the HolderView‘s layer before you animate in the fill.

Open ArcLayer.swift and add the following code to animate():

func animate() {
  var arcAnimationPre: CABasicAnimation = CABasicAnimation(keyPath: "path")
  arcAnimationPre.fromValue = arcPathPre.CGPath
  arcAnimationPre.toValue = arcPathStarting.CGPath
  arcAnimationPre.beginTime = 0.0
  arcAnimationPre.duration = animationDuration
    
  var arcAnimationLow: CABasicAnimation = CABasicAnimation(keyPath: "path")
  arcAnimationLow.fromValue = arcPathStarting.CGPath
  arcAnimationLow.toValue = arcPathLow.CGPath
  arcAnimationLow.beginTime = arcAnimationPre.beginTime + arcAnimationPre.duration
  arcAnimationLow.duration = animationDuration
    
  var arcAnimationMid: CABasicAnimation = CABasicAnimation(keyPath: "path")
  arcAnimationMid.fromValue = arcPathLow.CGPath
  arcAnimationMid.toValue = arcPathMid.CGPath
  arcAnimationMid.beginTime = arcAnimationLow.beginTime + arcAnimationLow.duration
  arcAnimationMid.duration = animationDuration
    
  var arcAnimationHigh: CABasicAnimation = CABasicAnimation(keyPath: "path")
  arcAnimationHigh.fromValue = arcPathMid.CGPath
  arcAnimationHigh.toValue = arcPathHigh.CGPath
  arcAnimationHigh.beginTime = arcAnimationMid.beginTime + arcAnimationMid.duration
  arcAnimationHigh.duration = animationDuration
    
  var arcAnimationComplete: CABasicAnimation = CABasicAnimation(keyPath: "path")
  arcAnimationComplete.fromValue = arcPathHigh.CGPath
  arcAnimationComplete.toValue = arcPathComplete.CGPath
  arcAnimationComplete.beginTime = arcAnimationHigh.beginTime + arcAnimationHigh.duration
  arcAnimationComplete.duration = animationDuration
    
  var arcAnimationGroup: CAAnimationGroup = CAAnimationGroup()
  arcAnimationGroup.animations = [arcAnimationPre, arcAnimationLow, arcAnimationMid, 
                                  arcAnimationHigh, arcAnimationComplete]
  arcAnimationGroup.duration = arcAnimationComplete.beginTime + arcAnimationComplete.duration
  arcAnimationGroup.fillMode = kCAFillModeForwards
  arcAnimationGroup.removedOnCompletion = false
  addAnimation(arcAnimationGroup, forKey: nil)
}

This animation is very similar to the earlier wobble animation; you create a CAAnimationGroup that contains five instances of a path-based CABasicAnimation. Each path has a slightly different arc with increasing height and is part of the starter project. Finally, you apply the CAAnimationGroup to the layer and instruct it to not be removed on completion so it will retain its state when the animation has finished.

Build and run your app to watch the magic unfold!

ContainerFillCompleted

Completing The Animation

All that’s left to do is expand the blue HolderView to fill in the entire screen and add a UILabel to the view to serve as the logo.

Open HolderView.swift and add the following code to the end of drawArc():

NSTimer.scheduledTimerWithTimeInterval(0.90, target: self, selector: "expandView", 
                                       userInfo: nil, repeats: false) 

This creates a timer that calls expandView() after the ArcLayer fills up the container.

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

func expandView() {
  // 1
  backgroundColor = Colors.blue

  // 2
  frame = CGRectMake(frame.origin.x - blueRectangleLayer.lineWidth, 
                     frame.origin.y - blueRectangleLayer.lineWidth, 
                     frame.size.width + blueRectangleLayer.lineWidth * 2, 
                     frame.size.height + blueRectangleLayer.lineWidth * 2)

  // 3
  layer.sublayers = nil
  
  // 4
  UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut,
    animations: {
      self.frame = self.parentFrame
    }, completion: { finished in
      self.addLabel()
    })
}

Here’s what that method does:

  1. The background of the holder view is set to blue, to match the color you filled the rectangle with.
  2. The frame is expanded to account for the RectangleLayer‘s stroke width that you added earlier.
  3. All sublayers are removed. Now there are no oval, no triangle and no rectangle layers.
  4. An animation is added to expand the HolderView to fill the screen. Once that animation’s done, you call addLabel().

Add the following function to the bottom of the class:

func addLabel() {
  delegate?.animateLabel()
}

This simply calls the view’s delegate function to animate the label.

Now open ViewController.swift and add the following code to animateLabel():

func animateLabel() {
  // 1
  holderView.removeFromSuperview()
  view.backgroundColor = Colors.blue

  // 2  
  var label: UILabel = UILabel(frame: view.frame)
  label.textColor = Colors.white
  label.font = UIFont(name: "HelveticaNeue-Thin", size: 170.0)
  label.textAlignment = NSTextAlignment.Center
  label.text = "S"
  label.transform = CGAffineTransformScale(label.transform, 0.25, 0.25)
  view.addSubview(label)

  // 3  
  UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: UIViewAnimationOptions.CurveEaseInOut,
    animations: ({
      label.transform = CGAffineTransformScale(label.transform, 4.0, 4.0)
    }), completion: { finished in
      self.addButton()
    })
}

Taking each commented section in turn:

  1. Remove HolderView from the view and set the view’s background color to blue.
  2. Create a UILabel with text of ‘S’ to represent the logo, and add it to the view.
  3. Apply a spring animation to the label to scale it in. Once the animation is done, call addButton() to add a button to your view, which, when pressed, repeats the animation.

Build and run the application, give yourself a pat on the back and take a moment to enjoy what you’ve built! :]

finalAnimationFullyComplete

Where to Go From Here?

You can download the final completed project here.

This tutorial covered quite a few different animation techniques that, when stacked together, create a rather complex loading animation that really makes your app shine on first run.

From here, feel free to play around with different timings and shapes to see what cool animations you can come up with.

If you want to take your new found animation skills to the next level, then I suggest you check out our book, iOS Animations by Tutorials.

I hope that you had a ton of fun going through this tutorial, and if you have any questions or comments, please join the forum discussion below!

Satraj

Contributors

Satraj

Author

Over 300 content creators. Join our team.