将CGGradient剪切到CGPath [英] Clipping a CGGradient to a CGPath

查看:93
本文介绍了将CGGradient剪切到CGPath的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在试图找出为什么这不工作,我一直在墙上撞墙。

I've been banging my head against the wall for a long while trying to figure out why this is not working.

基本上,我试图绘制一个图表(图表)与CGPath,然后使用它来剪辑渐变。最终效果应该像iPhone附带的Stocks应用程序一样。

Basically, I am trying to plot a graph (chart) with CGPath and then use that to clip a gradient. The end effect should be like the Stocks app which comes with the iPhone.

渐变和路径分别作为两个分层(未剪切)元素绘制。

The gradient and the path draw fine separately as two layered (unclipped) elements. But if I comment out the CGContextDrawPath, neither the line nor the gradient draw to the screen.

这是我的drawRect代码:

Here's my drawRect code:

CGContextRef context = UIGraphicsGetCurrentContext();

[[UIColor whiteColor] set];
CGContextSetLineWidth(context, 2.0f);
CGPoint lastDrawnPt = [[points objectAtIndex:0] CGPointValue];
CGPoint firstDrawnPt = lastDrawnPt;
//create the path for the gradient
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom left
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y);


for (int i=0; i<(points.count-1); i++) {
    //CGPoint pt1 = [[points objectAtIndex:i] CGPointValue];
    CGPoint pt2 = [[points objectAtIndex:i+1] CGPointValue];
    if (pt2.x > lastDrawnPt.x+2) {
        // only draw if we've moved sunstantially to the right

        //for the gradient
        CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y);
        CGPathAddLineToPoint(thePath, NULL, pt2.x, pt2.y);


        lastDrawnPt = pt2;
    }
}

//finish the gradient clipping path
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y);
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom right
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height);
CGPathAddLineToPoint(thePath, NULL, firstDrawnPt.x, self.bounds.size.height); // bottom right

CGPathCloseSubpath(thePath);

//add the gradient clipping path to the context
CGContextSaveGState(context);
CGContextAddPath(context, thePath);

//draw the path
float components[4] = {1.0, 1.0, 1.0, 1.0};
CGContextSetStrokeColor(context, components);
CGContextDrawPath(context,kCGPathStroke);

//clip the path
CGContextClip(context);


//Draw Gradient
UIColor *topColor = [UIColor colorWithRed: 1.0 green:1.0 blue:1.0 alpha:1.0];
UIColor *bottomColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.0];
CGColorRef colorRef[] = { [topColor CGColor], [bottomColor CGColor] };
CFArrayRef colors = CFArrayCreate(NULL, (const void**)colorRef, sizeof(colorRef) / sizeof(CGColorRef), &kCFTypeArrayCallBacks);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colors, NULL);
CFRelease(colorSpace);
CFRelease(colors);

//  Draw a linear gradient from top to bottom
CGPoint gradStartPoint = CGPointMake(50.0, self.bounds.size.height);
CGPoint gradEndPoint = CGPointMake(50.0, 0.0);
CGContextDrawLinearGradient(context, gradient, gradStartPoint, gradEndPoint, 0);

CFRelease(gradient);

// Cleanup
CGColorSpaceRelease(colorSpace);

CGContextRestoreGState(context);


推荐答案


CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom left
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y);


for (int i=0; i<(points.count-1); i++) {
    //CGPoint pt1 = [[points objectAtIndex:i] CGPointValue];
    CGPoint pt2 = [[points objectAtIndex:i+1] CGPointValue];
    if (pt2.x > lastDrawnPt.x+2) {
        // only draw if we've moved sunstantially to the right

        //for the gradient
        CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y);
        CGPathAddLineToPoint(thePath, NULL, pt2.x, pt2.y);


        lastDrawnPt = pt2;
    }
}

//finish the gradient clipping path
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y);
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom right
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height);
CGPathAddLineToPoint(thePath, NULL, firstDrawnPt.x, self.bounds.size.height); // bottom right


这只是绘制了一系列线段。对于某些点值,它可能看起来像这样:

This just plots a series of line segments. For certain point values, it might look something like this:

| |||| |

它不绘制单个连续的形状。因此,这个路径对于剪切是无用的,因为它实际上是空的;

It does not plot a single continuous shape. As such, this path is useless for clipping, as it is effectively empty; as you've seen, clipping to it will result in no further drawing being inside the clipping path.

每个 lineto curveto arc 等。您最初使用 moveto 设置,但每个 lineto curveto arc 等不会清除当前点,它会更新 。因此,要创建一个单一的形状,你需要做一个 moveto ,然后是一系列 lineto (或 curveto arc ),最后是 closepath

Every lineto, curveto, arc, etc. works from the current point. You set that initially with moveto, but each lineto, curveto, arc, etc. does not clear the current point, it updates it. Thus, to create a single shape, you do one moveto followed by a succession of lineto (or curveto or arc), followed eventually by closepath.

说到 closepath ...


CGPathCloseSubpath(thePath);


这会在路径中创建唯一的闭合形状,形状具有零区域(只是一个线段本身加倍),它仍然没有帮助剪辑的目的。

This creates the only closed shape in the path, but since that shape has zero area (being only a line segment that doubles back on itself), it is still not helpful for clipping purposes.

我怀疑你需要做的是剪切 moveto 段中的至少一个(循环中的一个,如果不是也在其后)。然后,您可以简化数组上的循环使用快速枚举,而不是使用索引,并剪切跟踪最后绘制点。

I suspect that all you need to do is cut out at least one of the moveto segments (the one in the loop, if not also the one after it). Then, you can simplify the loop—use fast enumeration on the array, instead of using indexes, and cut out keeping track of the "last drawn point".

NSArray索引的正确类型是 NSUInteger ,而不是 int 。保持你的类型匹配 - 它避免了后路疼痛。

Also, the correct type for indexes into an NSArray is NSUInteger, not int. Keep your types matched—it avoids pain later down the road.

这篇关于将CGGradient剪切到CGPath的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆