How to fill a UIBezierPath with a gradient?

ios, uibezierpath

Solution

The easiest approach is to use the original graph bezier path to help you construct a mask and impose this mask on a gradient layer. Now impose the graph layer on top of the gradient layer.

So, for example, make a CAShapeLayer, close the graph bezier path, set its path as the shape layer's path, and fill it black. Now you have a mask that is the shape of the area under the graph. Now make a CAGradientLayer and make the CAShapeLayer its `mask`. In front of that, place the actual graph.

So for example (EDITED):

Here's the code I used to create that drawing (my bezier path is very simple, just four points joined by three lines, but you can see clearly that the area under it is a gradient):

CAShapeLayer* shape = [[CAShapeLayer alloc] init];
shape.frame = self.graph.bounds;
CGFloat h = shape.frame.size.height;
CGFloat w = shape.frame.size.width;

NSArray* points = @[
                    [NSValue valueWithCGPoint:CGPointMake(0,h-50)],
                    [NSValue valueWithCGPoint:CGPointMake(70,h-100)],
                    [NSValue valueWithCGPoint:CGPointMake(140,h-75)],
                    [NSValue valueWithCGPoint:CGPointMake(w,h-150)],
                    ];

UIBezierPath* p = [UIBezierPath new];
[p moveToPoint:[points[0] CGPointValue]];
for (NSInteger i = 1; i < points.count; i++)
    [p addLineToPoint:[points[i] CGPointValue]];

shape.path = p.CGPath;
shape.strokeColor = [UIColor blackColor].CGColor;
shape.lineWidth = 2;
shape.fillColor = nil;

CAGradientLayer* grad = [[CAGradientLayer alloc] init];
grad.frame = self.graph.bounds;
grad.colors = @[(id)[UIColor blueColor].CGColor,
                (id)[UIColor yellowColor].CGColor];

CAShapeLayer* mask = [[CAShapeLayer alloc] init];
mask.frame = self.graph.bounds;
[p addLineToPoint:CGPointMake(w,h)];
[p addLineToPoint:CGPointMake(0,h)];
[p closePath];
mask.path = p.CGPath;
mask.fillColor = [UIColor blackColor].CGColor;
grad.mask = mask;

[self.graph.layer addSublayer:grad];
[self.graph.layer addSublayer:shape];

Problem

I've drawn a graph using a UIBezierPath. I can fill the area under the graph with a solid color but I want to fill the area under the graph with a gradient rather than with a solid color. But I'm not sure how to make the gradient apply only to the graph and not to the whole view, I've read a few questions but not found anything applicable. This is the main graph drawing code: ``` // Draw the graph UIBezierPath *barGraph = [UIBezierPath bezierPath]; barGraph.lineWidth = 1.0f; [blueColor setStroke]; TCDataPoint *dataPoint = self.testData[0]; CGFloat x = [self convertTimeToXPoint:dataPoint.time]; CGFloat y = [self convertDataToYPoint:dataPoint.dataUsage]; CGPoint plotPoint = CGPointMake(x,y); [barGraph moveToPoint:plotPoint]; for (int ii = 1; ii < [self.testData count]; ++ii) { dataPoint = self.testData[ii]; x = [self convertTimeToXPoint:dataPoint.time]; y = [self convertDataToYPoint:dataPoint.dataUsage]; plotPoint = CGPointMake(x, y); [barGraph addLineToPoint:plotPoint]; } [barGraph stroke]; ``` I've been attempting to fill the graph by experimenting with code like the following, but to be honest am not %100 sure what I'm doing despite going through various tutorials and documentation: ``` [barGraph closePath]; CGFloat colors [] = { 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2); CGColorSpaceRelease(baseSpace); CGPoint startPoint = CGPointMake(CGRectGetMidX(self.bounds), self.topY); CGPoint endPoint = CGPointMake(CGRectGetMidX(self.bounds), self.bottomY); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextClip(context); CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); CGGradientRelease(gradient); ```

Original source

Related problems