1

I'm currently trying to implement a circle with an animated stroke - which works fine (see image below).

For a while now, I've been trying to add a gradient to the circle's stroke. I'm am using a piece of code I already have used to underline a UITextField.

How can I apply my code for the gradient to the shapeLayer's stroke?

Circle:

let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1)

let trackLayer = CAShapeLayer()
let center = CGPoint(x: circleView.frame.size.width/2, y: circleView.frame.size.height / 1.3)
let circularPath = UIBezierPath(arcCenter: center, radius: 120, startAngle: CGFloat.pi, endAngle: 2 * CGFloat.pi, clockwise: true)

trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.groupTableViewBackground.cgColor
trackLayer.lineWidth = 14
trackLayer.fillColor = UIColor.clear.cgColor

shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = 15
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeEnd = 0

circleView.layer.addSublayer(trackLayer)
circleView.layer.addSublayer(shapeLayer)

Code Gradient:

let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1).cgColor
let sndColor = UIColor(red: 124/255, green: 206/255, blue: 254/255, alpha: 1).cgColor

let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [color, sndColor]
gradient.locations = [0.0, 0.8]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
let width = CGFloat(3.0)
gradient.frame = CGRect(x: 0, y: self.frame.size.height - width, width:  self.frame.size.width, height: self.frame.size.height)

self.layer.insertSublayer(gradient, at: 0)
self.layer.masksToBounds = true

Current Version:
Example

Edit:
My stroke animation:

func animateStroke() {
        if !animated {
            animated = true

            let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
            var value: Double?

            if expectedCosts <= monthlyPrice {
                value = 1
            } else {
                value = monthlyPrice / expectedCosts
            }

            basicAnimation.toValue = value
            basicAnimation.duration = 1.5
            basicAnimation.fillMode = CAMediaTimingFillMode.forwards
            basicAnimation.isRemovedOnCompletion = false
            basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)


            shapeLayer.add(basicAnimation, forKey: "strokeAnimation")

        }
    }

Stroke code:

func displayCircle() {
        let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1)

        let trackLayer = CAShapeLayer()
        let center = CGPoint(x: circleView.frame.size.width/2, y: circleView.frame.size.height / 1.3)
        let circularPath = UIBezierPath(arcCenter: center, radius: 120, startAngle: CGFloat.pi, endAngle: 2 * CGFloat.pi, clockwise: true)

        trackLayer.path = circularPath.cgPath
        trackLayer.strokeColor = UIColor.groupTableViewBackground.cgColor
        trackLayer.lineWidth = 14
        trackLayer.fillColor = UIColor.clear.cgColor

        shapeLayer.path = circularPath.cgPath
        shapeLayer.strokeColor = color.cgColor
        shapeLayer.lineWidth = 15
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeEnd = 0

        circleView.layer.addSublayer(trackLayer)
        circleView.layer.addSublayer(shapeLayer)

        let sndColor = UIColor(red: 124/255, green: 206/255, blue: 254/255, alpha: 1).cgColor

        let gradient: CAGradientLayer = CAGradientLayer()
        gradient.colors = [color, sndColor]
        gradient.locations = [0.0, 0.8]
        gradient.startPoint = CGPoint(x: 0, y: 0)
        gradient.endPoint = CGPoint(x: 1, y: 0)
        gradient.frame = circleView.bounds

        gradient.mask = trackLayer
        circleView.layer.addSublayer(gradient)
    }
Tom
  • 3,672
  • 6
  • 20
  • 52
  • Possible duplicate of [Applying a Gradient to CAShapeLayer](https://stackoverflow.com/questions/4733966/applying-a-gradient-to-cashapelayer) – PGDev May 13 '19 at 12:39
  • https://stackoverflow.com/questions/41437581/how-to-fill-a-bezier-path-with-gradient-color/41440222 – PGDev May 13 '19 at 12:40

1 Answers1

0
    let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1)
    let trackLayer = CAShapeLayer()
    let center = CGPoint(x: masterlayer.frame.size.width/2, y: masterlayer.frame.size.height / 1.3)
    let circularPath = UIBezierPath(arcCenter: center, radius: 120, startAngle: CGFloat.pi, endAngle: 2 * CGFloat.pi, clockwise: true)

    trackLayer.path = circularPath.cgPath
    trackLayer.strokeColor = UIColor.red.cgColor
    trackLayer.lineWidth = 14
    trackLayer.fillColor = UIColor.clear.cgColor

    trackLayer.strokeEnd = 0
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.duration = 3
    animation.fromValue = 0
    animation.toValue = 1
    animation.beginTime =  0.00001
    animation.fillMode = CAMediaTimingFillMode.forwards
    animation.isRemovedOnCompletion = false
    trackLayer.add(animation, forKey: "strokeEnd" + "\(index)")


    let color1 = UIColor(red: 11/255, green: 95/255, blue: 24/255, alpha: 1).cgColor
    let sndColor = UIColor(red: 124/255, green: 206/255, blue: 254/255, alpha: 1).cgColor
    let gradient: CAGradientLayer = CAGradientLayer()
    gradient.colors = [color1, sndColor]
    gradient.locations = [0.0, 0.8]
    gradient.startPoint = CGPoint(x: 0, y: 0)
    gradient.endPoint = CGPoint(x: 1, y: 0)
    let width = CGFloat(3.0)
    gradient.frame = masterlayer.bounds

    gradient.mask = trackLayer
    circleView.layer.addSublayer(gradient)

Working code for you problem

Add gradient on circleView.layer and set tracker layer as mask layer for graient

Jagveer Singh
  • 2,258
  • 19
  • 34
  • Thanks! Please find the new code + my animation inside of the edit - with this version, the stroke in the background has a gradient, but I want the animated `shapeLayer`'s stroke to have a gradient. Btw what do you mean by `masterLayer` - `circleView`? – Tom May 13 '19 at 13:12
  • yes , please replace masterLayer with circleView.layer – Jagveer Singh May 13 '19 at 13:17
  • gradient.mask = trackLayer circleView.layer.addSublayer(gradient) – Jagveer Singh May 13 '19 at 13:18
  • this is main thing – Jagveer Singh May 13 '19 at 13:18
  • Using `gradient.mask = trackLayer` applies the gradient to the whole stroke - can I somehow apply it only to the stroke in the foreground (blue stroke) which doesn't fill the whole shape's stroke. You can find my code in the edit to my initial question. – Tom May 13 '19 at 13:21