# Breaking Down a Complex iOS Animation: Objective-C and Swift

Language: en
Canonical: [Breaking Down a Complex iOS Animation: Objective-C and Swift](<https://allluckly.cn/en/journal/2016-07-04-breaking-down-a-complex-ios-animation-objective-c-and-swift/>)
中文：[中文 HTML](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao52>)
English: [English HTML](<https://allluckly.cn/en/journal/2016-07-04-breaking-down-a-complex-ios-animation-objective-c-and-swift/>)
Published: 2016-07-04T00:00:00+08:00
Modified: 2026-10-23T10:34:15+08:00
Author: HenryCheng

Breaks a welcome-screen animation into circular scaling, elliptical compression, edge bulges, rotation, and a wave fill. The article sequences layer and path changes to show how a visual effect can be translated into implementable animation steps.

Originally credited to HenryCheng; edited and archived by Bison.

[Source listed in the original](<http://www.jianshu.com/p/658641c77f51>)

English translation of the [Chinese original](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao52>), translated on 2026-10-23. The article body uses automated translation; app descriptions, historical reading notes and video subtitles receive separate editorial checks. Original publication dates, attribution, code and version notes are retained.

**Before you read** This technical note dates from 2015–2017. The examples have not been rerun as part of this archive update. Published in 2016, the article explains the implementation in Swift and also provides an Objective-C example.

## 1. Introduction

As developer numbers grow and time passes, the AppStore accumulates more and more apps, with many new releases every year. Yet people keep only a limited number on their phones, making user attraction an important part of product design. Impressive animation is one way to achieve this, and many good apps use it effectively. Animation can also be daunting: a good result takes time, patience, and a sound approach. Using the interesting example below, let us break it down and see how it is implemented.

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/1.gif>)

Editor: [Bison](<https://allluckly.cn/en/>)  
 Contributor: [HenryCheng](<http://www.jianshu.com/p/658641c77f51>)

## 2. Analysis

The animation above looks complicated at first. Break it down, though, and it is not so difficult. Looking closely reveals these broad steps:

1. A circle appears. 2. It is compressed horizontally and vertically into ellipses, then returns to a circle. 3. Small protrusions emerge in sequence at its lower-left, lower-right, and top. 4. The combined shape rotates once and becomes a triangle. 5. Two thick lines emerge successively from the triangle's left, enclosing it in a rectangle. 6. A wave fills the rectangle from bottom to top. 7. The filled rectangle expands to full screen, and Welcome appears. These are the main stages. We will implement them individually. The explanation below uses Swift; the demo includes both Objective-C and Swift implementations.

## 3. Animating between a circle and ellipses

After creating a project, add an AnimationView subclass of UIView to display the animation. First add CircleLayer, a circular layer, and animate it growing from small to large.

```
 class AnimationView: UIView {

      let circleLayer = CircleLayer()

      override init(frame: CGRect) {
           super.init(frame: frame)
           backgroundColor = UIColor.clearColor()
           addCircleLayer()
       }

      required init?(coder aDecoder: NSCoder) {
           super.init(coder: aDecoder)
       }

      /**
       add circle layer
       */
      func addCircleLayer() {
           self.layer.addSublayer(circleLayer)
           circleLayer.expand()
       }
  }
```

The expand() method is as follows.

```
/**
 expand animation function
 */
func expand() {
    let expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
    expandAnimation.fromValue = circleSmallPath.CGPath
    expandAnimation.toValue = circleBigPath.CGPath
    expandAnimation.duration = KAnimationDuration
    expandAnimation.fillMode = kCAFillModeForwards
    expandAnimation.removedOnCompletion = false
    self.addAnimation(expandAnimation, forKey: nil)
}
```

The result is shown below.

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/2.gif>)

With the first step complete, move on to the elliptical deformation. Think of squeezing an elastic ball vertically and then horizontally. This is simply a grouped animation:

```
/**
 wobbl group animation
 */
func wobbleAnimate() {
    // 1、animation begin from bigPath to verticalPath
    let animation1: CABasicAnimation = CABasicAnimation(keyPath: "path")
    animation1.fromValue = circleBigPath.CGPath
    animation1.toValue = circleVerticalSquishPath.CGPath
    animation1.beginTime = KAnimationBeginTime
    animation1.duration = KAnimationDuration

    // 2、animation vertical to horizontal
    let  animation2: CABasicAnimation = CABasicAnimation(keyPath: "path")
    animation2.fromValue = circleVerticalSquishPath.CGPath
    animation2.toValue = circleHorizontalSquishPath.CGPath
    animation2.beginTime = animation1.beginTime + animation1.duration
    animation2.duration = KAnimationDuration

    // 3、animation horizontal to vertical
    let  animation3: CABasicAnimation = CABasicAnimation(keyPath: "path")
    animation3.fromValue = circleHorizontalSquishPath.CGPath
    animation3.toValue = circleVerticalSquishPath.CGPath
    animation3.beginTime = animation2.beginTime + animation2.duration
    animation3.duration = KAnimationDuration

    // 4、animation vertical to bigPath
    let  animation4: CABasicAnimation = CABasicAnimation(keyPath: "path")
    animation4.fromValue = circleVerticalSquishPath.CGPath
    animation4.toValue = circleBigPath.CGPath
    animation4.beginTime = animation3.beginTime + animation3.duration
    animation4.duration = KAnimationDuration

    // 5、group animation
    let animationGroup: CAAnimationGroup = CAAnimationGroup()
    animationGroup.animations = [animation1, animation2, animation3, animation4]
    animationGroup.duration = 4 * KAnimationDuration
    animationGroup.repeatCount = 2
    addAnimation(animationGroup, forKey: nil)
}
```

The code above combines circle → ellipse with a horizontal major axis → ellipse with a vertical major axis → circle into one animation. The result is:

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/3.gif>)

## 4. Making the circle's edges protrude

The protruding shapes may look complicated at first, but the principle is simple. Look closely: joining their three tips forms a triangle. First, add a triangular layer to the existing shape, as follows.

```
import UIKit

class TriangleLayer: CAShapeLayer {

    let paddingSpace: CGFloat = 30.0

    override init() {
         super.init()
        fillColor = UIColor.colorWithHexString("#009ad6").CGColor
        strokeColor = UIColor.colorWithHexString("#009ad6").CGColor
        lineWidth = 7.0
        path = smallTrianglePath.CGPath
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    var smallTrianglePath: UIBezierPath {
        let smallPath = UIBezierPath()
        smallPath.moveToPoint(CGPointMake(5.0 + paddingSpace, 95.0))
        smallPath.addLineToPoint(CGPointMake(50.0, 12.5 + paddingSpace))
        smallPath.addLineToPoint(CGPointMake(95.0 - paddingSpace, 95.0))
        smallPath.closePath()
        return smallPath
    }
}
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/4.png>)

Then set the corner radius.

```
    lineCap = kCALineCapRound
    lineJoin = kCALineJoinRound
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/5.png>)  
 Now make the protrusion. The principle is simple: keep the triangle's center fixed and extend its left side farther left.

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/6.gif>)

Then, keeping the center fixed, stretch it to the right and upward in sequence in the same way.

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/7.gif>)

The process looks like this.

```
/**
 triangle animate function
 */
func triangleAnimate() {
     // left
    let triangleAnimationLeft: CABasicAnimation = CABasicAnimation(keyPath: "path")
    triangleAnimationLeft.fromValue = smallTrianglePath.CGPath
    triangleAnimationLeft.toValue = leftTrianglePath.CGPath
    triangleAnimationLeft.beginTime = 0.0
    triangleAnimationLeft.duration = 0.3
     // right
    let triangleAnimationRight: CABasicAnimation = CABasicAnimation(keyPath: "path")
    triangleAnimationRight.fromValue = leftTrianglePath.CGPath
    triangleAnimationRight.toValue = rightTrianglePath.CGPath
    triangleAnimationRight.beginTime = triangleAnimationLeft.beginTime + triangleAnimationLeft.duration
    triangleAnimationRight.duration = 0.25
     // top
    let triangleAnimationTop: CABasicAnimation = CABasicAnimation(keyPath: "path")
    triangleAnimationTop.fromValue = rightTrianglePath.CGPath
    triangleAnimationTop.toValue = topTrianglePath.CGPath
    triangleAnimationTop.beginTime = triangleAnimationRight.beginTime + triangleAnimationRight.duration
    triangleAnimationTop.duration = 0.20
     // group
    let triangleAnimationGroup: CAAnimationGroup = CAAnimationGroup()
    triangleAnimationGroup.animations = [triangleAnimationLeft, triangleAnimationRight, triangleAnimationTop]
    triangleAnimationGroup.duration = triangleAnimationTop.beginTime + triangleAnimationTop.duration
    triangleAnimationGroup.fillMode = kCAFillModeForwards
    triangleAnimationGroup.removedOnCompletion = false
    addAnimation(triangleAnimationGroup, forKey: nil)
}
```

Next, change the triangle's color.

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/8.gif>)

With matching colors, the protrusion becomes visible. Restore normal speed—I slowed it down for the demonstration—and combine all the preceding actions. The result so far is:

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/9.gif>)

So far it looks good, and we are almost halfway through. On to the next step!

## 5. Implementing rotation and the rectangle

Rotation is simple and probably familiar. Rotate the shape formed above, taking care to set its anchorPoint.

```
/**
 self transform z
 */
func transformRotationZ() {
    self.layer.anchorPoint = CGPointMake(0.5, 0.65)
    let rotationAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
    rotationAnimation.toValue = CGFloat(M_PI * 2)
    rotationAnimation.duration = 0.45
    rotationAnimation.removedOnCompletion = true
    layer.addAnimation(rotationAnimation, forKey: nil)
}
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/10.gif>)

After rotation, the original shape becomes a triangle. The idea is to clip the original large circle using the large triangle's incircle.

```
/**
 contract animation function
 */
func contract() {
    let contractAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
    contractAnimation.fromValue = circleBigPath.CGPath
    contractAnimation.toValue = circleSmallPath.CGPath
    contractAnimation.duration = KAnimationDuration
    contractAnimation.fillMode = kCAFillModeForwards
    contractAnimation.removedOnCompletion = false
    addAnimation(contractAnimation, forKey: nil)
}
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/11.gif>)

Next, draw the rectangle. Create a RectangleLayer and draw the lines.

```
/**
 line stroke color change with custom color

 - parameter color: custom color
 */
func strokeChangeWithColor(color: UIColor) {
    strokeColor = color.CGColor
    let strokeAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")
    strokeAnimation.fromValue = 0.0
    strokeAnimation.toValue = 1.0
    strokeAnimation.duration = 0.4
    addAnimation(strokeAnimation, forKey: nil)
}
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/12.gif>)

Finally comes the familiar wave animation. Here is the code directly.

```
func animate() {
     /// 1
    let waveAnimationPre: CABasicAnimation = CABasicAnimation(keyPath: "path")
    waveAnimationPre.fromValue = wavePathPre.CGPath
    waveAnimationPre.toValue = wavePathStarting.CGPath
    waveAnimationPre.beginTime = 0.0
    waveAnimationPre.duration = KAnimationDuration
     /// 2
    let waveAnimationLow: CABasicAnimation = CABasicAnimation(keyPath: "path")
    waveAnimationLow.fromValue = wavePathStarting.CGPath
    waveAnimationLow.toValue = wavePathLow.CGPath
    waveAnimationLow.beginTime = waveAnimationPre.beginTime + waveAnimationPre.duration
    waveAnimationLow.duration = KAnimationDuration
     /// 3
    let waveAnimationMid: CABasicAnimation = CABasicAnimation(keyPath: "path")
    waveAnimationMid.fromValue = wavePathLow.CGPath
    waveAnimationMid.toValue = wavePathMid.CGPath
    waveAnimationMid.beginTime = waveAnimationLow.beginTime + waveAnimationLow.duration
    waveAnimationMid.duration = KAnimationDuration
     /// 4
    let waveAnimationHigh: CABasicAnimation = CABasicAnimation(keyPath: "path")
    waveAnimationHigh.fromValue = wavePathMid.CGPath
    waveAnimationHigh.toValue = wavePathHigh.CGPath
    waveAnimationHigh.beginTime = waveAnimationMid.beginTime + waveAnimationMid.duration
    waveAnimationHigh.duration = KAnimationDuration
     /// 5
    let waveAnimationComplete: CABasicAnimation = CABasicAnimation(keyPath: "path")
    waveAnimationComplete.fromValue = wavePathHigh.CGPath
    waveAnimationComplete.toValue = wavePathComplete.CGPath
    waveAnimationComplete.beginTime = waveAnimationHigh.beginTime + waveAnimationHigh.duration
    waveAnimationComplete.duration = KAnimationDuration
     /// group animation
    let arcAnimationGroup: CAAnimationGroup = CAAnimationGroup()
    arcAnimationGroup.animations = [waveAnimationPre, waveAnimationLow, waveAnimationMid, waveAnimationHigh, waveAnimationComplete]
    arcAnimationGroup.duration = waveAnimationComplete.beginTime + waveAnimationComplete.duration
    arcAnimationGroup.fillMode = kCAFillModeForwards
    arcAnimationGroup.removedOnCompletion = false
    addAnimation(arcAnimationGroup, forKey: nil)
}
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/13.gif>)

Choose control points for the wave and draw a Bezier curve. That nearly completes the effect. The final step is to expand it and display Welcome.

```
func expandView() {
    backgroundColor = UIColor.colorWithHexString("#40e0b0")
    frame = CGRectMake(frame.origin.x - blueRectangleLayer.lineWidth,
                       frame.origin.y - blueRectangleLayer.lineWidth,
                       frame.size.width + blueRectangleLayer.lineWidth * 2,
                       frame.size.height + blueRectangleLayer.lineWidth * 2)
    layer.sublayers = nil

    UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
        self.frame = self.parentFrame
        }, completion: { finished in
            self.delegate?.completeAnimation()
    })

}
```

After expansion, set the delegate and add the Welcome label in the main vc.

```
// MARK: -
// MARK: AnimationViewDelegate
func completeAnimation() {
    // 1
    animationView.removeFromSuperview()
    view.backgroundColor = UIColor.colorWithHexString("#40e0b0")

    // 2
    let label: UILabel = UILabel(frame: view.frame)
    label.textColor = UIColor.whiteColor()
    label.font = UIFont(name: "HelveticaNeue-Thin", size: 50.0)
    label.textAlignment = NSTextAlignment.Center
    label.text = "Welcome"
    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.addTouchButton()
    })

}
```

All the animation is now complete.

![1](<https://allluckly.cn/images/blog/tuogao/tougao52/14.gif>)

## 6. Finally

As before, both Objective-C and Swift versions are provided. You can find them at [Here,](<https://github.com/Loveway/HWLoadingAnimation>) View the source!

I have always been interested in animation and hope to study it more deeply. Leave a comment or private message with suggestions, and please star the project if you like it. Thank you!

---

Recommended reading: [Adding a launch advertisement to an iOS app](<https://allluckly.cn/en/journal/2016-06-12-adding-a-launch-advertisement-to-an-ios-app/>)

