在 iOS 上使用 CGContext 绘制渐变线

Mat*_*ijs 5 gradient objective-c cgcontext polyline

我正在寻找一种使用 CGContextRef 沿其路径绘制具有线性渐变的线的方法。我尝试了几种方法,但大多数都专注于用渐变填充区域(矩形),这不是我需要的。

我使用以下代码使用 CGPathRef 绘制实线。我实际上需要的是一个平滑的线性渐变,从线条的第一个点的 color1 开始,到线条的最后一个点的 color2 结束。

有谁知道如何实现这一目标?

- (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);
}
Run Code Online (Sandbox Code Playgroud)

=========更新========================================== ========

我实际上发现这个问题也与我需要的非常相似:Gradient Polyline with MapKit ios

我使用以下代码来实现沿着线条路径的渐变。作为评论,不要介意获取和传递路径点的低效方式。稍后我会优化一下:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

这几乎产生了我所需要的。不过只有一点小故障: https://i.stack.imgur.com/F9Efx.jpg

正如您所看到的,行结尾是重叠的,这通常不是问题。但因为线条是透明的,所以我得到了这种不需要的副作用。

有人知道如何从这里出发吗?