I am trying to make a label with an animated gradient as its colour. I can get a static gradient just fine. I was playing around with animated gradients in a view with the intention of using a mask but that isn't happy with our custom label (although I think I've just realised why it wasn't working... I am thinking the solution I am currently working on is cleaner than a view). I did manage to get a nice seamless look using positions [-2, -1, 0, 1] and then animating to [0, 1, 2, 3].
Fast forward to where I started creating a label and it using the same gradient and passing setting that gradient as a color via the following extension methods:
extension UIColor {
convenience init(layer: CALayer) {
let ctx = UIGraphicsImageRenderer(size: layer.bounds.size)
let img = ctx.image { layer.render(in: $0.cgContext) }
self.init(patternImage: img)
}
}
It does not show. I compared it to what I had with my static gradient label and the only difference was the fact that I added positions (and of course the animation). I decided not to call the animate function which starts the animation and it was still invisible. I then commented out the position and all of a sudden it became visible. I then attempted to put the positions back and get rid of the negatives and values above 1 and it was visible. I then wondered if it was only outside the animation that it didn't like numbers outside [0,1] and the moment I start the animation it's gone again. made the positions inside the animation inside [0,1] and even then the animation isn't happy.
Here is my label class:
class AnimatedGradientLabel: BaseLabel {
private let startColor: UIColor = Color.gradientOrange
private let endColor: UIColor = Color.gradientPink
private let duration: CFTimeInterval = 4.0
private let repeatCount: Float = Float.infinity
var font: Font = Font.regularText
private lazy var gradient: CAGradientLayer = {
let gradientLayer = CAGradientLayer()
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
gradientLayer.colors = [
// startColor.cgColor,
// endColor.cgColor,
startColor.cgColor,
endColor.cgColor
]
gradientLayer.locations = [
// -2, -1, 0, 1 // not happy
// 0, 0, 0, 1 // happy provided I don't call animate()
0, 1 // happy provided I don't call animate()
] as [NSNumber]
return gradientLayer
}()
override var bounds: CGRect {
didSet {
gradient.frame = bounds
textColor = UIColor(layer: gradient)
}
}
override func style() {
super.style()
font = Font.header
}
func animate() {
gradient.colors = [
endColor.cgColor,
startColor.cgColor,
endColor.cgColor,
startColor.cgColor
]
let anim = CABasicAnimation(keyPath: "locations")
anim.fromValue = [-2, -1, 0, 1]
anim.toValue = [0, 1, 2, 3]
anim.duration = duration
anim.repeatCount = repeatCount
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
gradient.removeAllAnimations()
gradient.add(anim, forKey: nil)
}
}
If anyone has any suggestions, please I would really appreciate it
EDIT: Animation does set up there are only two colours, and is happy with both negative numbers and numbers above 1, but I noticed it doesn't animate.
EDIT2: Weirdly Just got it working with multiple colours. Not entirely sure what I was doing wrong initially. What I did also notice is the reason its not animating. That UIColor convenience initializer makes a pattern out of the gradient. It doesn't use the gradient as is. DOH! Okay looks like I might need to go back to the drawing board