How to fill a path with gradient in drawRect:?

38,890

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);
}
Share:
38,890
Derrick
Author by

Derrick

Updated on July 09, 2022

Comments

  • Derrick
    Derrick almost 2 years

    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.

  • Derrick
    Derrick almost 14 years
    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.
  • Nico
    Nico almost 14 years
    Derrick: The Quartz 2D Programming Guide covers that, along with everything else: developer.apple.com/mac/library/documentation/GraphicsImagin‌​g/…
  • Doron
    Doron almost 14 years
    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.
  • daveMac
    daveMac over 11 years
    @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.
  • ArtOfWarfare
    ArtOfWarfare over 11 years
    @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.
  • Victor Engel
    Victor Engel about 11 years
    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.
  • Alex Zavatone
    Alex Zavatone almost 3 years
    Updated location of Quartz 2D Programming Guide - developer.apple.com/library/archive/documentation/…