42

filling a path with a solid color is easy enough:

CGPoint aPoint;
for (id pointValue in points)
{
    aPoint = [pointValue CGPointValue];
    CGContextAddLineToPoint(context, aPoint.x, aPoint.y);
}
[[UIColor redColor] setFill];
[[UIColor blackColor] setStroke];
CGContextDrawPath(context, kCGPathFillStroke);

I'd like to draw a gradient instead of solid red, but I am having trouble. I've tried the code listed in the Question/Answer: Gradients on UIView and UILabels On iPhone

which is:

CAGradientLayer *gradient = [CAGradientLayer layer];
[gradient setFrame:rect];
[gradient setColors:[NSArray arrayWithObjects:(id)[[UIColor blueColor] CGColor],
                (id)[[UIColor whiteColor] CGColor], nil]];
[[self layer] setMasksToBounds:YES];

[[self layer] insertSublayer:gradient atIndex:0];

However, this paints the entire view that this is in with the gradient, covering up my original path.

Community
  • 1
  • 1
Derrick
  • 2,356
  • 5
  • 32
  • 43

1 Answers1

99

I would clip to the path you want to fill, and use CGContextDrawLinearGradient. Here is a simple implementation of drawRect: as an example:

- (void) drawRect:(CGRect)rect
{
    // Create a gradient from white to red
    CGFloat colors [] = { 
        1.0, 1.0, 1.0, 1.0, 
        1.0, 0.0, 0.0, 1.0
    };

    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2);
    CGColorSpaceRelease(baseSpace), baseSpace = NULL;

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSaveGState(context);
    CGContextAddEllipseInRect(context, rect);
    CGContextClip(context);

    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));

    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    CGGradientRelease(gradient), gradient = NULL;

    CGContextRestoreGState(context);

    CGContextAddEllipseInRect(context, rect);
    CGContextDrawPath(context, kCGPathStroke);
}
sbooth
  • 16,646
  • 2
  • 55
  • 81
  • 3
    If I could give you an awesome badge I would! Do you know of any tutorials on the CGContextSaveGState and stuff? Because the apple class reference is kind of thin. – Derrick Jun 06 '10 at 20:04
  • Derrick: The Quartz 2D Programming Guide covers that, along with everything else: http://developer.apple.com/mac/library/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_overview/dq_overview.html#//apple_ref/doc/uid/TP30001066-CH202-TPXREF132 – Peter Hosey Jun 06 '10 at 20:50
  • 1
    After using your example code, my shadow has disappeared. I've tried relocating the shadow line ("CGContextSetShadow(context, CGSizeMake(5.0, -5.0), 4);") before the gstate save, after it, befroe the gstate restore, after it, before and after the clipping - nothing helped. – Doron Jul 03 '10 at 12:35
  • 1
    @Doron Once you fill or stroke (I believe even clip) a path, that path is essentially nil and subsequent fills or strokes do nothing. You need to copy the path first. – daveMac Oct 17 '12 at 21:38
  • @Doron - Expanding on what daveMac said, I found I had to call `CGContextAddPath` (with my copied path from before) between my calls to FillPath and Clip. – ArtOfWarfare Jan 01 '13 at 17:46
  • I just found out about CGPathCreateCopyByStrokingPath, availabel in iOS 5 and later that creates a CGPath around an existing path. This should simplify drawing the gradient you're after. – Victor Engel Apr 29 '13 at 17:44
  • Updated location of Quartz 2D Programming Guide - https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html – Alex Zavatone Jul 24 '21 at 20:10