Line drawing with gradient using CGContext in iOS

I'm looking for a way to draw a linear gradient line along a path using CGContextRef. I've tried several approaches, but most of them focus on filling the rect with a gradient, which is not what I want.

I am using the following code to draw a solid line using CGPathRef. I really want a smooth linear gradient starting with color1 at the first point of the line and ending with color2 at the last point of the line.

Does anyone know how to achieve this?

- (CGMutablePathRef)generatedPath
{
    CGMutablePathRef path = CGPathCreateMutable();

    CGPathMoveToPoint(path, nil, 20, 10);
    CGPathAddLineToPoint(path, nil, 100, 100);
    CGPathAddLineToPoint(path, nil, 140, 120);
    CGPathAddLineToPoint(path, nil, 160, 100);
    CGPathAddLineToPoint(path, nil, 210, 70);
    CGPathAddLineToPoint(path, nil, 250, 10);

    return path;
}

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGMutablePathRef path = [self generatedPath];
    CGContextAddPath(ctx, path);

    CGColorRef color = [UIColor redColor].CGColor;

    CGContextSetLineWidth(ctx, 8.0);

    CGContextSetStrokeColorWithColor(ctx, color);
    CGContextStrokePath(ctx);
}

      

========= UPDATE ============================= ========== ========

I actually found this question, which is also very similar to what I need: Gradient polyline with MapKit ios

I used the following code to achieve a gradient along the path of the line. As a side note, ignore the inefficient way to get and go around waypoints. I will optimize this later:

- (NSArray*)generatePathPoints {
    NSMutableArray *points = [[NSMutableArray alloc] init];

    [points addObject:[NSValue valueWithCGPoint:CGPointMake(20, 50)]];
    [points addObject:[NSValue valueWithCGPoint:CGPointMake(100, 100)]];
    [points addObject:[NSValue valueWithCGPoint:CGPointMake(140, 120)]];
    [points addObject:[NSValue valueWithCGPoint:CGPointMake(160, 100)]];
    [points addObject:[NSValue valueWithCGPoint:CGPointMake(210, 70)]];
    [points addObject:[NSValue valueWithCGPoint:CGPointMake(250, 10)]];

    return points;
}

- (CGMutablePathRef)pathFromPoints: (NSArray*)points;
{
    CGMutablePathRef path = CGPathCreateMutable();
    for (int i = 0; i < points.count; i++) {
        NSValue *pointValue = points[i];
        CGPoint point = [pointValue CGPointValue];
        if(i == 0){
            CGPathMoveToPoint(path, nil, point.x, point.y);
        }
        else{
            CGPathAddLineToPoint(path, nil, point.x, point.y);
        }
    }

    return path;
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    NSArray *points = [self generatePathPoints];

    CGFloat widthOfLine = 8.0;
    CGLineCap lineCap = kCGLineCapRound;
    CGLineJoin lineJoin = kCGLineJoinRound;
    int miterLimit = 10;

    UIColor *ccolor, *pcolor;
    for (int i=0;i< points.count;i++){
        CGMutablePathRef path = CGPathCreateMutable();
        CGPoint point = ((NSValue*)points[i]).CGPointValue;
        ccolor = [self colorForPointAtIndex:i points:points];
        if (i==0){
            CGPathMoveToPoint(path, nil, point.x, point.y);
        } else {
            CGPoint prevPoint = ((NSValue*)points[i-1]).CGPointValue;
            CGPathMoveToPoint(path, nil, prevPoint.x, prevPoint.y);
            CGPathAddLineToPoint(path, nil, point.x, point.y);
            CGFloat pc_r,pc_g,pc_b,pc_a,
            cc_r,cc_g,cc_b,cc_a;
            [pcolor getRed:&pc_r green:&pc_g blue:&pc_b alpha:&pc_a];
            [ccolor getRed:&cc_r green:&cc_g blue:&cc_b alpha:&cc_a];
            CGFloat gradientColors[8] = {pc_r,pc_g,pc_b,pc_a,
                cc_r,cc_g,cc_b,cc_a};

            CGFloat gradientLocation[2] = {0,1};
            CGContextSaveGState(context);
            CGFloat lineWidth = CGContextConvertSizeToUserSpace(context, (CGSize){widthOfLine,widthOfLine}).width;
            CGPathRef pathToFill = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, lineCap, lineJoin, miterLimit);
            CGContextAddPath(context, pathToFill);
            CGContextClip(context);//<--clip your context after you SAVE it, important!
            CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
            CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocation, 2);
            CGColorSpaceRelease(colorSpace);
            CGPoint gradientStart = prevPoint;
            CGPoint gradientEnd = point;
            CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, kCGGradientDrawsAfterEndLocation | kCGGradientDrawsBeforeStartLocation);
            CGGradientRelease(gradient);
            CGContextRestoreGState(context);//<--Don't forget to restore your context.
        }
        pcolor = [UIColor colorWithCGColor:ccolor.CGColor];
    }
}

-(UIColor*) colorForPointAtIndex: (NSUInteger)index points:(NSArray*)points {
    return [UIColor colorWithRed:1.0 green:0 blue:0 alpha:(1.0 / points.count) * index];
}

      

This is COMPLETELY what I need. There is only a tiny glitch though: http://imgur.com/gn0JE5z

As you can see, line borders overlap, which is usually not a problem. But since the lines are transparent, I get this unwanted side effect.

Does anyone know how to go from here?

+3


source to share





All Articles