Swift CAReplicatorLayer Sample Code

Created By: Debasis Das (29-May-2017)
In this post we will see how CAReplicatorLayer works and create a couple of sample code to play around with CAReplicatorLayer
CAReplicatorLayer is essentially a layer that creates a specified number of copies of its sublayers, each copy potentially having geometric, temporal, and color transformations applied to it.

We can use a CAReplicatorLayer object to build complex layouts based on a single source layer that is replicated with transformation rules that can affect the position, rotation color, and time.

In this post we will create the below samples
Swift_CAReplicatorLayer_Nesting_1
Swift_CAReplicatorLayer_Nesting_2
Swift_CAReplicatorLayer_ProgressIndicator_1
Swift_CAReplicatorLayer_ProgressIndicator_2
Swift_CAReplicatorLayer_ProgressIndicator_3

To begin with we have added a root CALayer to our view and have set the background color of the root layer to black

import Cocoa
class ViewController: NSViewController {
    let rootLayer = CALayer()
    override func viewDidLoad() {
        super.viewDidLoad()
        rootLayer.frame = self.view.frame
        rootLayer.backgroundColor = NSColor.black.cgColor
        self.view.layer = rootLayer
        self.view.wantsLayer = true
        
        //replicatorLayerSampleOne()
        //replicatorLayerSampleTwo()
        //replicatorLayerSampleThree()
        //progressIndicatorSampleOne()
        //progressIndicatorSampleTwo()
        //progressIndicatorSampleThree()
    }
//Individual Functions to demonstrate the CAReplicatorLayer
}

Simple CAReplicatorLayer example
Swift_CAReplicatorLayer_1

This creates a square of size 50 * 50 and replicates that 5 times and each instance has a color offset.
Each of the instance is added to the replicator layer at a horizontal distance of 10

    func replicatorLayerSampleOne(){
        let replicatorLayer = CAReplicatorLayer()
        let coloredSquare = CALayer()
        coloredSquare.backgroundColor = NSColor.white.cgColor
        coloredSquare.frame = CGRect(x: 10, y: 10, width: 50, height: 50)
        
        let instanceCount = 5
        replicatorLayer.instanceCount = instanceCount
        replicatorLayer.instanceTransform = CATransform3DMakeTranslation(60, 0, 0)
        
        replicatorLayer.instanceGreenOffset = -0.2
        replicatorLayer.addSublayer(coloredSquare)
        rootLayer.addSublayer(replicatorLayer)
        
    }

Nesting replicator Layers in X and Y Axis

    
func replicatorLayerSampleTwo(){
        let replicatorLayer = CAReplicatorLayer()
        let rowLayer = CAReplicatorLayer()
        
        replicatorLayer.instanceCount = 4
        rowLayer.instanceCount = 5
        
        let coloredSquare = CALayer()
        coloredSquare.backgroundColor = NSColor.white.cgColor
        coloredSquare.frame = CGRect(x: 10, y: 10, width: 50, height: 50)
        
        rowLayer.instanceTransform = CATransform3DMakeTranslation(60, 0, 0)
        rowLayer.addSublayer(coloredSquare)
        rowLayer.instanceGreenOffset = -0.2
        
        replicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, 60, 0)
        replicatorLayer.instanceRedOffset = -0.2
        replicatorLayer.addSublayer(rowLayer)
        
        rootLayer.addSublayer(replicatorLayer)
        
    }

Nesting replicator Layers in X, Y and Z axis

    
    func replicatorLayerSampleThree(){
        let replicatorLayer = CAReplicatorLayer()
        let rowLayer = CAReplicatorLayer()
        let zLayer = CAReplicatorLayer()
        
        replicatorLayer.instanceCount = 4
        rowLayer.instanceCount = 5
        zLayer.instanceCount = 5
        
        let coloredSquare = CALayer()
        coloredSquare.backgroundColor = NSColor.white.cgColor
        coloredSquare.frame = CGRect(x: 10, y: 10, width: 50, height: 50)
        
        zLayer.instanceTransform = CATransform3DMakeTranslation(10, 5, -20)
        zLayer.instanceBlueOffset = -0.2
        zLayer.instanceRedOffset = -0.1
        
        zLayer.addSublayer(coloredSquare)
        
        rowLayer.instanceTransform = CATransform3DMakeTranslation(90, 0, 0)
        rowLayer.addSublayer(zLayer)
        rowLayer.instanceGreenOffset = -0.2
        
        replicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, 90, 0)
        replicatorLayer.instanceRedOffset = -0.2
        replicatorLayer.addSublayer(rowLayer)
        
        rootLayer.addSublayer(replicatorLayer)
        
    }

Progress Indicator using CAReplicatorLayer – opacity animation

    
    func progressIndicatorSampleOne(){
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame = CGRect(x: 20, y: 20, width: 200, height: 200)
        replicatorLayer.borderColor = NSColor.white.cgColor
        replicatorLayer.cornerRadius = 5.0
        replicatorLayer.borderWidth = 1.0
        
        let circle = CALayer()
        circle.frame = CGRect(origin: CGPoint.zero,
                              size: CGSize(width: 20, height: 20))
        circle.backgroundColor = NSColor.blue.cgColor
        circle.cornerRadius = 10
        circle.position = CGPoint(x: 20, y: 100)
        replicatorLayer.addSublayer(circle)
        
        let fadeOut = CABasicAnimation(keyPath: "opacity")
        fadeOut.fromValue = 1
        fadeOut.toValue = 0
        fadeOut.duration = 1
        fadeOut.repeatCount = Float.greatestFiniteMagnitude
        circle.add(fadeOut, forKey: nil)
        
        
        let instanceCount = 20
        replicatorLayer.instanceCount = instanceCount
        replicatorLayer.instanceDelay = fadeOut.duration / CFTimeInterval(instanceCount)
        
        let angle = -CGFloat.pi * 2 / CGFloat(instanceCount)
        replicatorLayer.instanceTransform = CATransform3DMakeRotation(angle, 0, 0, 1)
        rootLayer.addSublayer(replicatorLayer)
    }

Progress Indicator using CAReplicatorLayer – scale animation

    
    func progressIndicatorSampleTwo(){
        
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame = CGRect(x: 20, y: 20, width: 200, height: 200)
        replicatorLayer.borderColor = NSColor.white.cgColor
        replicatorLayer.cornerRadius = 5.0
        replicatorLayer.borderWidth = 1.0
        
        let circle = CALayer()
        circle.frame = CGRect(origin: CGPoint.zero,
                              size: CGSize(width: 20, height: 20))
        circle.backgroundColor = NSColor.blue.cgColor
        circle.cornerRadius = 10
        circle.position = CGPoint(x: 20, y: 100)
        replicatorLayer.addSublayer(circle)
        
        let shrinkAnimation = CABasicAnimation(keyPath: "transform.scale")
        shrinkAnimation.fromValue = 1
        shrinkAnimation.toValue = 0.1
        shrinkAnimation.duration = 1
        shrinkAnimation.repeatCount = Float.greatestFiniteMagnitude
        circle.add(shrinkAnimation, forKey: nil)

        
        let instanceCount = 20
        replicatorLayer.instanceCount = instanceCount
        replicatorLayer.instanceDelay = shrinkAnimation.duration / CFTimeInterval(instanceCount)
        
        let angle = -CGFloat.pi * 2 / CGFloat(instanceCount)
        replicatorLayer.instanceTransform = CATransform3DMakeRotation(angle, 0, 0, 1)
        rootLayer.addSublayer(replicatorLayer)
   }

Progress Indicator using CAReplicatorLayer – opacity and scale animation

    
func progressIndicatorSampleThree(){
        
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame = CGRect(x: 20, y: 20, width: 200, height: 200)
        replicatorLayer.borderColor = NSColor.white.cgColor
        replicatorLayer.cornerRadius = 5.0
        replicatorLayer.borderWidth = 1.0
        
        let circle = CALayer()
        circle.bounds = CGRect(x: 0, y: 0, width: 30, height: 8)
        circle.backgroundColor = NSColor.blue.cgColor
        circle.cornerRadius = 2
        circle.position = CGPoint(x: 20, y: 100)
        replicatorLayer.addSublayer(circle)
        
        let fadeOut = CABasicAnimation(keyPath: "opacity")
        fadeOut.fromValue = 1
        fadeOut.toValue = 0
        fadeOut.duration = 1
        fadeOut.repeatCount = Float.greatestFiniteMagnitude
        circle.add(fadeOut, forKey: nil)
        
        let shrinkAnimation = CABasicAnimation(keyPath: "transform.scale")
        shrinkAnimation.fromValue = 1
        shrinkAnimation.toValue = 0.1
        shrinkAnimation.duration = 1
        shrinkAnimation.repeatCount = Float.greatestFiniteMagnitude
        
        circle.add(fadeOut, forKey: nil)
        circle.add(shrinkAnimation, forKey: nil)
        
        
        let instanceCount = 20
        replicatorLayer.instanceCount = instanceCount
        replicatorLayer.instanceDelay = 1 / CFTimeInterval(instanceCount)
        
        let angle = -CGFloat.pi * 2 / CGFloat(instanceCount)
        replicatorLayer.instanceTransform = CATransform3DMakeRotation(angle, 0, 0, 1)
        rootLayer.addSublayer(replicatorLayer)
    }

You can download the source code from here CAReplicatorLayerSampleCode

Posted in Swift, Swift 3.1 Tagged with: , ,

Leave a Reply

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

*