Search code examples
iosxcodeswiftcore-animation

CALayer Subclass Repeating Animation


I'm attempting to create a CALayer subclass that performs an animation every x seconds. In the example below I'm attempting to change the background from one random color to another but when running this in the playground nothing seems to happen

import UIKit
import XCPlayground
import QuartzCore

let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200, height: 200))
XCPShowView("view", view)

class CustomLayer: CALayer {

    var colors = [
        UIColor.blueColor().CGColor,
        UIColor.greenColor().CGColor,
        UIColor.yellowColor().CGColor
    ]

    override init!() {
        super.init()

        self.backgroundColor = randomColor()

        let animation = CABasicAnimation(keyPath: "backgroundColor")

        animation.fromValue = backgroundColor
        animation.toValue = randomColor()
        animation.duration = 3.0
        animation.repeatCount = Float.infinity

        addAnimation(animation, forKey: "backgroundColor")

    }

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

    private func randomColor() -> CGColor {
        let index = Int(arc4random_uniform(UInt32(colors.count)))
        return colors[index]
    }
}

let layer = CustomLayer()
layer.frame = view.frame
view.layer.addSublayer(layer)

Solution

  • The parameters of a repeating animation are only setup once, so you can't change the color on each repeat. Instead of a repeating animation, you should implement the delegate method, animationDidStop:finished:, and call the animation again from there with a new random color. I haven't tried this in a playground, but it works ok in an app. Notice that you have to implement init!(layer layer: AnyObject!) in addition to the other init methods you had.

    import UIKit
    
    class CustomLayer: CALayer {
    
        var newColor: CGColorRef!
    
        var colors = [
            UIColor.blueColor().CGColor,
            UIColor.greenColor().CGColor,
            UIColor.yellowColor().CGColor
        ]
    
        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        override init!(layer: AnyObject!) {
            super.init(layer: layer)
        }
    
        override init!() {
            super.init()
            backgroundColor = randomColor()
            newColor = randomColor()
            self.animateLayerColors()
        }
    
    
        func animateLayerColors() {
            let animation = CABasicAnimation(keyPath: "backgroundColor")
            animation.fromValue = backgroundColor
            animation.toValue = newColor
            animation.duration = 3.0
            animation.delegate = self
    
            addAnimation(animation, forKey: "backgroundColor")
        }
    
        override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
            backgroundColor = newColor
            newColor = randomColor()
            self.animateLayerColors()
        }
    
    
        private func randomColor() -> CGColor {
            let index = Int(arc4random_uniform(UInt32(colors.count)))
            return colors[index]
        }
    }