Created CAGradientLayer with a sharp color transition. Position of the color transition changes during the time with using CABasicAnimation.
Everything works, but if I use long duration, animation doesn't produce a smooth change.
Code of gradient creation
override func draw(_ rect: CGRect) {
let path = CGMutablePath()
path.addRect(CGRect(x: 0, y: 0, width: 50, height: 50))
path.move(to: CGPoint(x: 75, y: 75))
path.addRect(CGRect(x: 75, y: 75, width: 50, height: 50))
let mask = CAShapeLayer()
mask.path = path
gradient = CAGradientLayer()
gradient.mask = mask
gradient.colors = [UIColor.orange.cgColor, UIColor.blue.cgColor]
gradient.locations = [0, 0]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.frame = bounds
layer.insertSublayer(gradient, at: 0)
}
Animation code
func play() {
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0, 0]
animation.toValue = [1, 1]
animation.duration = 120
animation.fillMode = .backwards
animation.isRemovedOnCompletion = false
gradient.add(animation, forKey: nil)
}
Demonstration of how the code works https://i.sstatic.net/eUvOo.gif
Are there any options to make the changes smooth with long animation duration?
This looks like a limitation of CAGradientLayer
You can check current progress of CABasicAnimation
using layer presentation()
.
And using CADisplayLink
you can check updated value which presentation layer has for each frame, something like this:
...
gradient.add(animation, forKey: nil)
let link = CADisplayLink(target: self, selector: #selector(linkHandler))
link.add(to: .main, forMode: .default)
link.isPaused = false
}
@objc func linkHandler() {
NSLog("\(gradient.presentation()?.locations)")
}
From this test you'll see that value gest updated totally fine, but CAGradientLayer
rounds them to around 0.004
. Imagine that inside there's code like
locations = locations.map { round($0 * 250) / 250 }
I doubt there's anything you can do about that.
What you can do is use Metal do draw gradient by hand. It's a big and kind of scary topic, but drawing gradient isn't that hard. Check out Rainbows, it's outdated you will have to update the syntax to the current one, but it should be possible