CAGradient not showing when locations are <0 or >1

31 views Asked by At

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

0

There are 0 answers