Why is UIView animation not working when starting right after view is added?

1.5k Views Asked by At

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?

1

There are 1 best solutions below

1
beyowulf On BEST ANSWER

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.