【问题标题】:Clipping a CGGradient to a CGPath将 CGGradient 裁剪为 CGPath
【发布时间】:2011-06-15 13:16:46
【问题描述】:

我一直在用头撞墙很长时间试图弄清楚为什么这不起作用。

基本上,我正在尝试使用 CGPath 绘制图形(图表),然后使用它来剪辑渐变。最终效果应该像 iPhone 自带的 Stocks 应用。

渐变和路径分别绘制为两个分层(未剪辑)的元素。但是如果我注释掉 CGContextDrawPath,线条和渐变都不会绘制到屏幕上。

这是我的 drawRect 代码:

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);

【问题讨论】:

  • 您是否尝试过 CGContextFlush 或 CGContextSynchronize... 我认为这可能与分层有关。还可以尝试在绘制渐变后移动 CGContextClip...
  • 感谢您的建议,但它们似乎没有帮助。
  • 我开始认为我构建路径的方式有问题。
  • 当我将绘图路径线更改为 CGContextDrawPath(context,kCGPathFillStroke);它根本没有填满路径......这是一个线索吗?
  • 你能举例说明点数组包含什么吗?

标签: iphone cocoa-touch core-graphics quartz-2d


【解决方案1】:
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

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

| |||| |

它不会绘制单个连续形状。因此,这条路径对于剪裁毫无用处,因为它实际上是空的;如您所见,剪裁到它不会导致剪裁路径内没有进一步的绘图。

每个linetocurvetoarc 等都从当前点开始工作。您最初设置了moveto,但每个linetocurvetoarc等都没有清除当前点,它更新它。因此,要创建单个形状,您需要先创建一个 moveto,然后是连续的 lineto(或 curvetoarc),最后是 closepath

说到closepath……

CGPathCloseSubpath(thePath);

这会在路径中创建唯一的闭合形状,但由于该形状的面积为零(只是一条线段,会在自身上加倍),因此对于剪辑目的仍然没有帮助。

我怀疑您需要做的就是至少剪掉moveto 段中的一个(循环中的那个,如果不是它之后的那个)。然后,您可以简化循环——对数组使用快速枚举,而不是使用索引,并取消对“最后绘制点”的跟踪。

此外,NSArray 中索引的正确类型是NSUInteger,而不是int。保持你的类型匹配——它可以避免以后的痛苦。

【讨论】:

  • 非常感谢彼得!就是这样。我对 CGPath 的工作原理有一个危险的温和理解。
  • 关于 [NSArray objectAtIndex:i] 中的 NSUInteger 与 int,该方法不会自动将 int 转换为 NSUInteger 吗?
  • @juggleware:不,语言确实如此,但这并不意味着您不应该首先使用正确的类型。如果你使用了错误的类型,你最终可能无法计数足够远或计数太远(后者在溢出到负范围的情况下,当转换为像NSUInteger这样的无符号类型时,成为一个巨大的数字)。在整个过程中使用正确的类型可以避免这些问题。
猜你喜欢
  • 1970-01-01
  • 2011-02-03
  • 2021-11-29
  • 1970-01-01
  • 2013-10-22
  • 2013-10-14
  • 2011-09-25
  • 2022-09-24
  • 2019-01-11
相关资源
最近更新 更多