/ animation

Create a Rotation Animation for Album Artwork in Swift

We love minor animation, which is something we feel extraterrestrial. Today we want to show you the way we create a nicely animation for album artwork view inside our next project:


As you can see, the artwork is a circle image view, we also have to smaller circle at the center to create effect of a real CD. Then, we add a shadow to the image and finally, we make it rotate!

View Layer

Go to interface builder, and add an square UIImageView, then place two square view at center of this image:


Don’t forget to create outlets for those three views.

View Properties

Assume that we have outlets as follow:

@IBOutlet weak var diskContainer: UIView!
@IBOutlet weak var artworkDisk: UIImageView!
@IBOutlet weak var diskOuter: UIView!
@IBOutlet weak var diskInner: UIView!

Note that diskContainer is the view containing all three views above. We will need this because when we make our image view rounded, the shadow effect will be lost, so we have to apply shadow effect onto containing view.

Next, make image view and two inner views rounded, and add the shadow to outer container like following:

self.diskInner.layer.cornerRadius = 2
self.diskOuter.layer.cornerRadius = 8
self.diskOuter.layer.borderColor = UIColor(netHex: 0x8F8F8F, alpha: 1).CGColor
self.artworkDisk.layer.cornerRadius = 80
self.diskContainer.layer.cornerRadius = 80
self.diskContainer.layer.shadowColor = UIColor.blackColor().CGColor
self.diskContainer.layer.shadowOffset = CGSizeMake(0, 0)
self.diskContainer.layer.shadowRadius = 6
self.diskContainer.layer.shadowOpacity = 0.6
self.artworkContainer.layer.shadowColor = UIColor.blackColor().CGColor
self.artworkContainer.layer.shadowOffset = CGSizeMake(0, 4)
self.artworkContainer.layer.shadowRadius = 4
self.artworkContainer.layer.shadowOpacity = 0.6

Note the cornerRadius value is half of its size (width or height), so we can create a perfect circle.

Add Animation

We should set a beautiful image to our artwork UIImageView first:

self.artworkDisk.image = <your-image>

Our animation will be placed inside a function:

func runSpinAnimationOn(view: UIView, duration: Double, rotation: Double, repeat: Float) {
  let animation = CABasicAnimation(keyPath: "transform.rotation.z")
  animation.toValue = NSNumber(double: M_PI * 2.0 * rotation * duration)
  animation.duration = duration
  animation.cumulative = true
  animation.repeatCount = repeat
  animation.removedOnCompletion = false
  animation.fillMode = kCAFillModeForwards
  view.layer.addAnimation(animation, forKey: "rotationAnimation") }

Here we use a CABasicAnimation so we can control almost every aspects of the animation. We also add the animation to layer of a view, and allow caller to customize duration as well as rotation angle and repeat count.

To apply this animation to our artwork image, simply call:

self.runSpinAnimationOn(self.artworkDisk, duration: 1, rotation: M_PI / 2 / 60, repeat: MAXFLOAT)

We use MAXFLOAT for repeat to make our artwork spin forever, we also defining duration step by 1 second and rotation angle with value of M_PI divide by 120. Feel free to adjust those values to achieve rotation speed of the animation. Now you can run project and see your artwork spinning.

Resume and Pause Animation

Sometimes we want to pause the rotation and resume it later. Maybe in case of buffering. To do that, we save the offset time of animation, change animation speed to zero when we want it to be paused, and change it back to 1.0 when we want to resume it. Our pause and resume functions should be like this:

func pauseAnimation(layer: CALayer) {
  let pausedTime = layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
  layer.speed = 0
  layer.timeOffset = pausedTime

func resumeAnimation(layer: CALayer) {
  let pausedTime = layer.timeOffset
  if (pausedTime > 0) {
    layer.speed = 1
    layer.timeOffset = 0
    layer.beginTime = layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
  } else {
    self.runSpinAnimationOn(self.artworkDisk, duration: 1, rotation: M_PI / 2 / 60, repeat: MAXFLOAT)

You can see that inside resumeAnimation, we add check for pausedTime, if it is zero then we known the animation have not been started yet, so we call run spin animation of our view.

That is it! Feel free to tell us if you have any comments or questions!