我正在努力学习和理解CoreGraphics。我想做的是做一个饼图。
饼图工作正常,看起来很棒,但我在修剪内圈时遇到了麻烦。
这是饼图中每张幻灯片的代码:
CGPoint center = CGPointMake((self.bounds.size.width/2) + self.centerOffset, (self.bounds.size.height/2) - self.centerOffset);
CGFloat radius = MIN(center.x, center.y) - 25;
radius *= self.pieScale;
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, center.x, center.y);
CGPoint p1 = CGPointMake(center.x + radius * cosf(self.startAngle), center.y + radius * sinf(self.startAngle));
CGContextAddLineToPoint(ctx, p1.x, p1.y);
int clockwise = self.startAngle > self.endAngle;
CGContextAddArc(ctx, center.x, center.y, radius, self.startAngle, self.endAngle, clockwise);
CGContextClosePath(ctx);
CGContextMoveToPoint(ctx, center.x, center.y);
CGContextAddArc(ctx, center.x, center.y, radius*0.5, self.startAngle, self.endAngle, clockwise);
CGContextSetFillColorWithColor(ctx, self.fillColor.CGColor);
CGContextSetStrokeColorWithColor(ctx, self.strokeColor.CGColor);
CGContextSetLineWidth(ctx, self.strokeWidth);
self.pathRef = CGContextCopyPath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
我现在的馅饼看起来像这样:
我设法通过绘制半径较小的新路径来添加内圈。
我尝试使用CGContextClip(ctx);
修剪第二条路径,但只留下内圈如下:
对我来说是有道理的为什么会这样,但我无法想象我还应该做些什么。
修改
代码现在看起来像:
CGPoint center = CGPointMake((self.bounds.size.width/2) + self.centerOffset, (self.bounds.size.height/2) - self.centerOffset);
CGFloat radius = MIN(center.x, center.y) - 25;
radius *= self.pieScale;
CGPoint p1 = CGPointMake(center.x + radius * cosf(self.startAngle), center.y + radius * sinf(self.startAngle));
int clockwise = self.startAngle > self.endAngle;
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, center.x, center.y);
CGContextAddLineToPoint(ctx, p1.x, p1.y);
CGContextAddArc(ctx, center.x, center.y, radius, self.startAngle, self.endAngle, clockwise);
CGContextClosePath(ctx);
CGContextMoveToPoint(ctx, center.x, center.y);
CGContextAddArc(ctx, center.x, center.y, radius*0.5, self.startAngle, self.endAngle, !clockwise);
CGContextSetFillColorWithColor(ctx, self.fillColor.CGColor);
CGContextSetStrokeColorWithColor(ctx, self.strokeColor.CGColor);
CGContextSetLineWidth(ctx, self.strokeWidth);
self.pathRef = CGContextCopyPath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
看起来像:
所有绘图代码:
我的班级是CALayer的子类。这段代码在饼图中绘制了一个切片。
-(void)drawInContext:(CGContextRef)ctx
{
CGPoint center = CGPointMake((self.bounds.size.width/2) + self.centerOffset, (self.bounds.size.height/2) - self.centerOffset);
CGFloat radius = MIN(center.x, center.y) - 25;
radius *= self.pieScale;
int clockwise = self.startAngle > self.endAngle;
/* Clipping should be done first so the next path(s) are not creating the clipping mask */
CGContextMoveToPoint(ctx, center.x, center.y);
CGContextAddArc(ctx, center.x, center.y, radius*0.5, self.startAngle, self.endAngle, !clockwise);
//CGContextClipPath(ctx);
CGContextClip(ctx);
/* Now, start drawing your graph and filling things in... */
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, center.x, center.y);
CGPoint p1 = CGPointMake(center.x + radius * cosf(self.startAngle), center.y + radius * sinf(self.startAngle));
CGContextAddLineToPoint(ctx, p1.x, p1.y);
CGContextAddArc(ctx, center.x, center.y, radius, self.startAngle, self.endAngle, clockwise);
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx, self.fillColor.CGColor);
CGContextSetStrokeColorWithColor(ctx, self.strokeColor.CGColor);
CGContextSetLineWidth(ctx, self.strokeWidth);
self.pathRef = CGContextCopyPath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
// LABELS
UIGraphicsPushContext(ctx);
CGContextSetFillColorWithColor(ctx, self.labelColor.CGColor);
CGFloat distance = [self angleDistance:(self.startAngle * 180/M_PI) angle2:(self.endAngle * 180/M_PI)];
CGFloat arcDistanceAngle = distance * M_PI/180;
CGFloat arcCenterAngle = self.startAngle + arcDistanceAngle/2;
CGPoint labelPoint = CGPointMake(center.x + radius * cosf(arcCenterAngle), center.y + radius * sinf(arcCenterAngle));
/*
Basic drawing of lines to labels.. Disabled for now..
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, labelPoint.x, labelPoint.y);
*/
if(labelPoint.x <= center.x)
labelPoint.x -= 50;
else
labelPoint.x += 5;
if(labelPoint.y <= center.y)
labelPoint.y -= 25;
/*
Basic drawing of lines to labels.. Disabled for now..
CGContextAddLineToPoint(ctx, labelPoint.x, labelPoint.y);
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx, self.fillColor.CGColor);
CGContextSetStrokeColorWithColor(ctx, self.strokeColor.CGColor);
CGContextSetLineWidth(ctx, self.strokeWidth);
CGContextDrawPath(ctx, kCGPathFillStroke);
*/
[self.labelString drawAtPoint:labelPoint forWidth:50.0f withFont:[UIFont systemFontOfSize:18] lineBreakMode:NSLineBreakByClipping];
UIGraphicsPopContext();
}
答案 0 :(得分:7)
如果您不想拥有饼图的中心(即制作所谓的甜甜圈推车),那么我建议您创建各个甜甜圈片段的形状,而不是制作饼形切片并遮盖中心。
可能首先想到的是用两条直线和两条不同半径但相同中心坐标的圆弧来创建该分段。幸运的是,在Core Graphics中有更简单的方法来实现这种形状。这种形状实际上只是一个角度与另一个角度之间的单个弧形但更厚。我已经在this answer解释了这一切(关于“从圈子或甜甜圈中画出片段”的问题),但这里只是对甜甜圈片段形状的简短解释。
创建将位于圆环段中心的圆弧(下图中的橙色线)。半径为(rmax + rmin) / 2
。
CGMutablePathRef arc = CGPathCreateMutable();
CGPathMoveToPoint(arc, NULL,
startPoint.x, startPoint.y);
CGPathAddArc(arc, NULL,
centerPoint.x, centerPoint.y,
radius,
startAngle,
endAngle,
YES);
通过抚摸圆弧创建最终的圆环段形状。这个功能对你来说可能看起来很神奇,但这就是在Core Graphics中找到隐藏宝藏的感觉。它将以特定的笔划宽度描绘路径。 “线帽”和“线连接”控制形状的开始和结束以及路径组件之间的连接如何显示(此形状中只有一个组件)。
CGFloat lineWidth = 10.0; // any radius you want
CGPathRef donutSegment =
CGPathCreateCopyByStrokingPath(arc, NULL,
lineWidth,
kCGLineCapButt,
kCGLineJoinMiter, // the default
10); // 10 is default miter limit
像填充饼形一样填充此形状。 (图2(上图)中使用了lightGray和black)。
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextAddPath(c, donutSegment);
CGContextSetFillColorWithColor(c, [UIColor lightGrayColor].CGColor);
CGContextSetStrokeColorWithColor(c, [UIColor blackColor].CGColor);
CGContextDrawPath(c, kCGPathFillStroke);
答案 1 :(得分:2)
如果我正确理解你的问题,你想在中心有一个洞,你正试图使用剪贴蒙版来做到这一点。因此,您唯一需要做的就是反转绘制您尝试剪辑的内部路径的方向。然后,由于填充规则,它会为你剪掉一个漂亮的洞。
您的代码应如下所示:
CGPoint center = CGPointMake((self.bounds.size.width/2) + self.centerOffset, (self.bounds.size.height/2) - self.centerOffset);
CGFloat radius = MIN(center.x, center.y) - 25;
radius *= self.pieScale;
int clockwise = self.startAngle > self.endAngle;
/* Clipping should be done first so the next path(s) are not creating the clipping mask */
CGContextMoveToPoint(ctx, center.x, center.y);
/* Create outer path going clockwise */
CGContextAddArc(ctx, center.x, center.y, radius*3, 0, 2*M_PI, clockwise);
/* Create inner path / mask going counter-clockwise to make the 'hole' in the center */
CGContextAddArc(ctx, center.x, center.y, radius*0.5, 0, 2*M_PI, !clockwise);
CGContextClip(ctx);
/* Now, start drawing your graph and filling things in... */
CGContextBeginPath(ctx);
/* Here's the stroke of the inner circle */
CGPoint p1 = CGPointMake(center.x + radius/2 * cosf(self.endAngle), center.y + radius/2 * sinf(self.endAngle));
CGContextMoveToPoint(ctx, p1.x, p1.y);
CGContextAddArc(ctx, center.x, center.y, radius / 2 + 1, self.endAngle, self.startAngle, !clockwise);
p1 = CGPointMake(center.x + radius * cosf(self.startAngle), center.y + radius * sinf(self.startAngle));
CGContextAddLineToPoint(ctx, p1.x, p1.y);
CGContextAddArc(ctx, center.x, center.y, radius, self.startAngle, self.endAngle, clockwise);
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx, self.fillColor.CGColor);
CGContextSetStrokeColorWithColor(ctx, self.strokeColor.CGColor);
CGContextSetLineWidth(ctx, self.strokeWidth);
self.pathRef = CGContextCopyPath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
更新:我修复了代码并从github上的代码运行它并且可以正常运行。希望这能解决你的“漏洞”问题。 (原谅双关语)
以下是此技术示例的要点:https://gist.github.com/rcdilorenzo/6437406