Apply gradient color to arc created with UIBezierPath

You can use a CAGradientLayer to get the gradient effect, and use the CAShapeLayer as a mask.

e.g.:

- (void)viewDidLoad
{
    [super viewDidLoad];

    int radius = 100;

    CAShapeLayer *arc = [CAShapeLayer layer];    
    arc.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 50) radius:radius startAngle:60.0 endAngle:0.0 clockwise:YES].CGPath;

    arc.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
                               CGRectGetMidY(self.view.frame)-radius);

    arc.fillColor = [UIColor clearColor].CGColor;
    arc.strokeColor = [UIColor purpleColor].CGColor;
    arc.lineWidth = 15; 
    CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    drawAnimation.duration            = 5.0; // "animate over 10 seconds or so.."
    drawAnimation.repeatCount         = 1.0;  // Animate only once..
    drawAnimation.removedOnCompletion = NO;   // Remain stroked after the animation..
    drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    drawAnimation.toValue   = [NSNumber numberWithFloat:10.0f];
    drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    [arc addAnimation:drawAnimation forKey:@"drawCircleAnimation"];

    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = self.view.frame;
    gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor,(__bridge id)[UIColor blueColor].CGColor ];
    gradientLayer.startPoint = CGPointMake(0,0.5);
    gradientLayer.endPoint = CGPointMake(1,0.5);

    [self.view.layer addSublayer:gradientLayer];
     //Using arc as a mask instead of adding it as a sublayer.
     //[self.view.layer addSublayer:arc]; 
     gradientLayer.mask = arc;


}

enter image description here

To draw a gradient along a stroke, see this implementation: https://stackoverflow.com/a/43668420/917802

EDIT: In short, create a custom UIView class, add a radial gradient to it by iterating between two colours at increasing angles, e.g. colour1 = 1 degree, colour2 = 2 degrees etc, all the way up to 360. Then apply a donut mask to that. As you change the strokeEnd value of the masking CAShapeLayer, you also rotate the underlying radial gradient. Because they move together it looks like the stroke itself has a gradient.


The Answers given so far are great, but they are a bit complicated, I think the following logic should be much simpler:

  1. Draw your curve/path of your shape layer .
  2. Close the path on itself, i.e draw the same curve/path but in reverse.
  3. Create a CAShapeLayer and set its path to the path in (2).
  4. Give the shape layer in (3) an arbitrary stroke colour.
  5. Create a CAGradientLayer.
  6. Set the gradient layer mask to the shape layer in (4).
  7. Set the colours of the gradient layer to your desired array.
  8. Add the gradient layer to your original shapeLayer in (1).
func draweCurve(fromPoint: CGPoint, toPoint: CGPoint, x: CGFloat) {
    // ------- 1 --------
    let curveLayer = CAShapeLayer()
    curveLayer.contentsScale = UIScreen.main.scale
    curveLayer.frame = CGRect(origin: .zero, size: CGSize(width: 100, height: 100))
    curveLayer.fillColor = UIColor.red.cgColor
    curveLayer.strokeColor = UIColor.blue.cgColor

    let path = UIBezierPath()
    path.move(to: fromPoint)
    let controlPoint1 = CGPoint(x: fromPoint.x + 40 * 0.45, y: fromPoint.y)
    let controlPoint2 = CGPoint(x: toPoint.x - 40 * 0.45, y: toPoint.y)
    path.addCurve(to: toPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
    curveLayer.path = path.cgPath

    // ------- 2 --------
    // close the path on its self
    path.addCurve(to: fromPoint, controlPoint1: controlPoint2, controlPoint2: controlPoint1)
    addGradientLayer(to: curveLayer, path: path)
}

private func addGradientLayer(to layer: CALayer, path: UIBezierPath) {
    // ------- 3 --------
    let gradientMask = CAShapeLayer()
    gradientMask.contentsScale = UIScreen.main.scale
    // ------- 4 --------
    gradientMask.strokeColor = UIColor.white.cgColor
    gradientMask.path = path.cgPath

    // ------- 5 --------
    let gradientLayer = CAGradientLayer()
    // ------- 6 --------
    gradientLayer.mask = gradientMask
    gradientLayer.frame = layer.frame
    gradientLayer.contentsScale = UIScreen.main.scale
    // ------- 7 --------
    gradientLayer.colors = [UIColor.gray.cgColor, UIColor.green.cgColor]
    // ------- 8 --------
    layer.addSublayer(gradientLayer)
}