23

I am attemping to animate the CAGradientLayer using Swift. For a resource I'm using this post

Here is my code

class ViewController: UIViewController {

  var gradient : CAGradientLayer?;

  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func viewDidAppear(animated: Bool) {

    self.gradient = CAGradientLayer()
    self.gradient?.frame = self.view.bounds
    self.gradient?.colors = [ UIColor.redColor().CGColor, UIColor.redColor().CGColor]
    self.view.layer.insertSublayer(self.gradient, atIndex: 0)

    animateLayer()
  }

  func animateLayer(){

    var fromColors = self.gradient?.colors
    var toColors: [AnyObject] = [ UIColor.blueColor().CGColor, UIColor.blueColor().CGColor]

    var animation : CABasicAnimation = CABasicAnimation(keyPath: "colors")

    animation.fromValue = fromColors
    animation.toValue = toColors
    animation.duration = 3.00
    animation.removedOnCompletion = true
    animation.fillMode = kCAFillModeForwards
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    animation.delegate = self

    self.gradient?.addAnimation(animation, forKey:"animateGradient")
  }
}

I've attempted this in Objective-C and it works, but I need to use it in Swift and when I run the program it animates but goes back to the previous color at the end, I need it so remain blue.

Daniel
  • 20,420
  • 10
  • 92
  • 149
DrPatience
  • 1,736
  • 2
  • 15
  • 33

4 Answers4

32

I have checked your code and found one mistake here. You forgot to set color of self.gradient you are just giving colors to animation thats why animation is working fine and at the end you see its color goes back to red.

Write this missing code:

var toColors: [AnyObject] = [UIColor.blueColor().CGColor, UIColor.blueColor().CGColor]
self.gradient.colors = toColors // You missed this line
var animation : CABasicAnimation = CABasicAnimation(keyPath: "colors")

You can also check working code here: Sample

UPDATE: how do I repeat this gradient transition continuously ?

So you can use delegate of CAAnimation. It will let you know once the animation has been finished and then you can set the fromColors and toColors value and call the animateLayer() function again and again. But for this you will have to make few changes in your code.

  • Make fromColors and toColors global.
  • Change their value in animationDidStop delegate method.
  • Call the animateLayer() function again.

Here is the delegate method body:

override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {

    self.toColors = self.fromColors;
    self.fromColors = self.gradient?.colors
    animateLayer()
}

And you can also check working code here: Sample

TheTiger
  • 13,264
  • 3
  • 57
  • 82
  • how do I repeat this gradient transition continuously ? – DrPatience Aug 12 '15 at 09:39
  • Any idea why the animation begins to flicker/stutter & go crazy once you segue to another view and return back to it? – theflarenet May 24 '16 at 16:42
  • @theflarenet Please make sure you are not starting new animation while coming back. Avoid making new instance of this animation OR stop last animation then start new one. – TheTiger May 25 '16 at 04:30
  • 1
    @TheTiger Excellent work on developing the method of continuously repeating the animation; however, I am having trouble stopping the last animation in order to start a new one in this case. Returning to the segue interferes with the delegate and `animateLayer()`, and `removedOnCompletion` within. – theflarenet May 25 '16 at 17:05
  • Just a heads-up for anyone out there, `animationLayer()` will cause the CPU to spike up to 100% if you segue to another view. I'm unsure of how to stop all animations and put the whole initial view to a halt. Hope someone recognizes this; however, this is a nice workaround for a continuous transition. – theflarenet May 27 '16 at 02:49
  • @theflarenet I told you! Don't initialize anything again. Just change their values. Make them global and initialize once what you don't need to change. Just change values in that function. – TheTiger May 27 '16 at 04:33
  • @TheTiger I understand that I shouldn't initalize anything but what I don't understand is what I should exactly change to make the animation pause/stop--so to speak. `animation.duration = 0`? Also, I noticed that wrapping the `animationDidStop()` contents with a `if (flag)` solves the 100% CPU issue. Hope this helps someone! – theflarenet May 27 '16 at 18:03
  • 3
    Note that the animation object has a `repeatCount` and a `autoreverses` property that handles repetition quite nicely without the need for delegates or timers. – shim Jan 10 '17 at 20:37
6

For those in the comments asking about endless animations, here's my stock sample code for an endlessly animated background:-

The full discussion and results can be seen on my blog

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // create the gradient layer
        let gradient = CAGradientLayer()
        gradient.frame = self.view.bounds
        gradient.startPoint = CGPoint(x:0.0, y:0.5)
        gradient.endPoint = CGPoint(x:1.0, y:0.5)
        gradient.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
        gradient.locations =  [-0.5, 1.5]

        let animation = CABasicAnimation(keyPath: "colors")
        animation.fromValue = [UIColor.red.cgColor, UIColor.green.cgColor]
        animation.toValue = [UIColor.green.cgColor, UIColor.red.cgColor]
        animation.duration = 5.0
        animation.autoreverses = true
        animation.repeatCount = Float.infinity

        // add the animation to the gradient
        gradient.add(animation, forKey: nil)

        // add the gradient to the view
        self.view.layer.addSublayer(gradient)
    }
 }
Echelon
  • 7,306
  • 1
  • 36
  • 34
1

For future reference... The 100% CPU usage is caused by the animationDidStop function. It restarts the animation while the current animations isn't finished. Please check the finished flag:

override func animationDidStop(anim: CAAnimation, finished: Bool) {
    if finished {
        self.toColors = self.fromColors;
        self.fromColors = self.gradient?.colors

        animateLayer()
    }
}
1

I've attempted this in Objective-C and it works, but I need to use it in Swift and when I run the program it animates but goes back to the previous color at the end, I need it so remain blue.

You need just :

animation.removedOnCompletion = false // not true like in your code
Alaeddine
  • 6,104
  • 3
  • 28
  • 45