将 CGGradient 剪切到 CGPath
很长一段时间以来,我一直在用头撞墙,试图找出为什么这不起作用。
基本上,我试图用 CGPath 绘制图形(图表),然后使用它来剪辑渐变。最终效果应该像iPhone自带的股票应用程序一样。
渐变和路径分别作为两个分层(未剪切)元素进行精细绘制。但是,如果我注释掉 CGContextDrawPath,则线条和渐变都不会绘制到屏幕上。
这是我的绘制矩形代码:
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);
I've been banging my head against the wall for a long while trying to figure out why this is not working.
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.
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);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这只是绘制了一系列线段。对于某些点值,它可能看起来像这样:
它不绘制单个连续形状。因此,这条路径对于剪切来说是无用的,因为它实际上是空的;正如您所看到的,剪切到它将导致剪切路径内不再有任何绘图。
每个
lineto
、curveto
、arc
等都从当前点开始工作。您最初使用moveto
设置该值,但每个lineto
、curveto
、arc
、等等不会清除当前点,而是更新它。因此,要创建单个形状,您需要执行一次moveto
,然后执行一系列lineto
(或curveto
或arc
) >),最后是closepath
。说到
closepath
...这将创建路径中唯一的闭合形状,但由于该形状的面积为零(只是一条自身加倍的线段),因此它对于剪切目的仍然没有帮助。
我怀疑您需要做的就是剪掉至少一个 moveto 段(循环中的段,如果不是循环后的段)。然后,您可以简化循环 - 在数组上使用快速枚举,而不是使用索引,并消除对“最后绘制点”的跟踪。
另外,NSArray 索引的正确类型是 NSUInteger,而不是 int。保持你的类型匹配——这样可以避免日后的痛苦。
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.
Every
lineto
,curveto
,arc
, etc. works from the current point. You set that initially withmoveto
, but eachlineto
,curveto
,arc
, etc. does not clear the current point, it updates it. Thus, to create a single shape, you do onemoveto
followed by a succession oflineto
(orcurveto
orarc
), followed eventually byclosepath
.Speaking of
closepath
…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.
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”.Also, the correct type for indexes into an NSArray is
NSUInteger
, notint
. Keep your types matched—it avoids pain later down the road.