0

The animation of a UIView should start right after the view is added to the superview:

class myView: UIView {
    override func didMoveToSuperview() {
        super.didMoveToSuperview()

        UIView.animate(
            withDuration: duration,
            delay: 0,
            options: .curveLinear,
            animations: {
                CATransaction.begin()
                CATransaction.setAnimationDuration(duration)
                self.layer.colors = [color1, color2]
                CATransaction.commit()
        },
            completion: nil
        )
    }
}

However, the result is that the animation is already over when the view becomes visible in the superview. UIView.animate does not animate but sets self.layer.colors immediately, maybe because the view is not yet visible when didMoveToSuperview is called.

How can I make the animation start normally?

Manuel
  • 14,274
  • 6
  • 57
  • 130

1 Answers1

2

Animating gradients using core animation can be done by creating a CABasicAnimation and adding it to your CAGradientLayer. You don't need to wrap this in a UIView animation block and you can do this from viewDidMoveToSuperview provided that you add your UIView subclass instance to the view hierarchy sometime after your root view has been added to a UIWindow. For example, in a playground one can write:

import UIKit
import PlaygroundSupport

class MyView: UIView
{
    var duration = 20.0

    var fromColors = [UIColor.orange.cgColor, UIColor.blue.cgColor]
    var toColors = [UIColor.red.cgColor, UIColor.green.cgColor]

    override func didMoveToSuperview() {
        super.didMoveToSuperview()

        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = bounds
        layer.addSublayer(gradientLayer)

        let animation = CABasicAnimation(keyPath: "colors")
        animation.fromValue = fromColors
        animation.toValue = toColors
        animation.duration = duration
        gradientLayer.add(animation, forKey: nil)

        gradientLayer.colors = toColors
    }
}

class ViewController: UIViewController
{
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let myView = MyView(frame: view.bounds)
        view.addSubview(myView)
    }
}

PlaygroundPage.current.liveView = ViewController()

and see a 20 second animation from vertical orange to blue gradient animate to a vertical red to green gradient.

beyowulf
  • 15,101
  • 2
  • 34
  • 40
  • Thanks, it works fine now. The only thing to change is that the final state `gradientLayer.colors = toColors` should be set before adding the animation `gradientLayer.add(animation, forKey: nil)`. Otherwise it would flicker sometimes when doing continuous animation. And it is not even necessary anymore to start the animation in `didMoveToSuperview`, it works also in the `init` function. – Manuel Jul 02 '17 at 23:10