Categories
Manuals

The power of CAReplicatorLayer

A few months ago my friend Danil created an open source project for loading spinners, I remember having a look at the animations and thinking that some of them could have been easily made using CAReplicatorLayer. In this tutorial I will show you how to create some of Danil’s animations using CAReplicatorLayer.

The CAReplicatorLayer class creates a specified number of copies of its sublayers (the source layer), each copy potentially having geometric, temporal and color transformations applied to it.

Prerequisites / Requirements

  • Xcode 7
  • Swift 2.0
  • For this tutorial I will assume that you are already familiar with CABasicAnimation

Before we start you will need to create an xcodeproject using swift. For the purposes of the demo we can write the code in the viewDidLoad method of the ViewController, the final source code has been refactored and made reusable.

Pulse Animation

The first step is to create a shape that we want to duplicate. In this case we want a white filled circle.

        let pulse = CAShapeLayer()
        pulse.frame = CGRect(x: 0,y: 0,width: 80,height: 80)
        pulse.path = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: size, height: size)).CGPath
        pulse.fillColor = tintColor.CGColor

CAReplicatorLayer creates a number of copies of it’s sublayers. So by adding my original pulse layer as a sublayer of a CAReplicatorLayer, I will be able to duplicate the pulse layer.
Below are the properties of a CAReplicatorLayer that we will need to use:

  • instanceCount, number of copies of the original layer
  • instanceDelay, this will be explained later on for animations purposes
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame = CGRect(x: 0,y: 0,width: size,height: size)

        replicatorLayer.instanceDelay = 0.5
        replicatorLayer.instanceCount = 8
        //add the circle pulse layer to replicatorLayer
        replicatorLayer.addSublayer(pulse)
        //add the replicator layer to your main view layer        layer.addSublayer(replicatorLayer)

If you compile this code you will see a white circle. All we have at the moment is 8 pulse layers on top of each other. What we want is to animate the white circle from scale 0.0 to 1.0 and the white circle opacity going from 1.0 to 0.0. For this we need to create a group animation. This group animation will be added to the original pulse layer.

instanceDelay is used to define the delay between each layer’s animation.
if we want the pulse to feel like its looping infinitely for an animation of 4 seconds and delay of 0.5 we will need 8 instanceCount. 4/0.5 = 8

        let groupAnimation = CAAnimationGroup()
        groupAnimation.animations = [alphaAnimation(), scaleAnimation()]
        groupAnimation.duration = 4.0
        groupAnimation.autoreverses = false
        groupAnimation.repeatCount = HUGE

        pulse.addAnimation(groupAnimation, forKey: "groupAnimation")

The opacity and scale animation using CABasicAnimation:

    func alphaAnimation() -> CABasicAnimation{
        let alphaAnim = CABasicAnimation(keyPath: "opacity")
        alphaAnim.fromValue = NSNumber(float: 1.0)
        alphaAnim.toValue = NSNumber(float: 0.0)
        return alphaAnim
    }

    func scaleAnimation() -> CABasicAnimation{
        let scaleAnim = CABasicAnimation(keyPath: "transform")

        let t = CATransform3DIdentity
        let t2 = CATransform3DScale(t, 0.0, 0.0, 0.0)
        scaleAnim.fromValue = NSValue.init(CATransform3D: t2)
        let t3 = CATransform3DScale(t, 1.0, 1.0, 0.0)
        scaleAnim.toValue = NSValue.init(CATransform3D: t3)
        return scaleAnim
    }

If you build now, you will notice the original pulse layer appear before the animation. Let’s set the opacity value to 0.0 to hide it.

        replicatorLayer.addSublayer(pulse)
        //hide the original layer
        pulse.opacity = 0.0

Hope you enjoy this tutorial, the source code is also available in github. Feel free to contribute by adding new amazing animations 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *