使用核心图形绘制浮雕弧 [英] draw embossed arc using core graphics
问题描述
我正在尝试实现自定义滑块,如下图所示。
I am trying to implement a custom slider as shown in figure below.
到目前为止我所做的事情看起来像这样
what I have done so far looks something like this
请帮帮我用于绘制具有这种效果的弧。我的代码如下所示,我正在做的是使用线宽为kLineWidth的CGContextAddArc绘制弧。
please help me out for drawing the arc with such effect. my code is as below, what I am doing is drawing the arc using CGContextAddArc with line width kLineWidth.
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext: (CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);
CGImageRef imageRef = [UIImage imageNamed:@"circle25.png"].CGImage;
CGRect rect = CGRectMake(sliderButtonCenterPoint.x - kThumbRadius, sliderButtonCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
CGContextDrawImage(context, rect, imageRef);
//CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO);
CGContextFillPath(context);
UIGraphicsPopContext();
}
- (CGPoint)drawArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, M_PI/3);// 2*M_PI
CGFloat startAngle = (4*M_PI)/3;
CGFloat endAngle = startAngle + angleFromTrack;
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);
CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);
CGContextStrokePath(context);
UIGraphicsPopContext();
return arcEndPoint;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint middlePoint;
middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;
middlePoint.y = self.bounds.origin.y + self.bounds.size.width;
CGContextSetLineWidth(context, kLineWidth);
CGFloat radius = [self sliderRadius];
[self.maximumTrackTintColor setStroke];
[self drawArcTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];
[self.minimumTrackTintColor setStroke];
self.thumbCenterPoint = [self drawArcTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];
[self.thumbTintColor setFill];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
}
推荐答案
除非你要成为动态改变形状,你可能最好只在图像编辑器中创建图像。我知道在Photoshop,Illustrator或Fireworks中创建该效果很容易。
Unless you are going to be changing the shape dynamically, you would probably be better off just creating the image in an image editor. I know it's easy to create that effect in Photoshop, Illustrator, or Fireworks.
也就是说,使用Core Graphics绘制内部阴影需要几个步骤:
That said, drawing an inner shadow like that with Core Graphics requires several steps:
- 剪辑到形状(使用例如
CGContextClip
或CGContextClipToMask
)。 - 制作但形状的路径或掩码。
- 设置阴影参数(使用
CGContextSetShadowWithColor
)。 - 填充步骤2中的路径或掩码。这会在形状内部投射阴影,并且只有阴影被绘制,因为你在步骤1中剪切了形状。
- Clip to the shape (using e.g.
CGContextClip
orCGContextClipToMask
). - Make a path or mask of everything but the shape.
- Set your shadow parameters (using
CGContextSetShadowWithColor
). - Fill the path or mask from step 2. This casts a shadow inside the shape, and only the shadow is drawn because you clipped to the shape in step 1.
如果你正确地完成所有这些,你可以得到一个很好的结果像这样:
If you do all of that correctly, you can get a nice result like this:
这是我写的用来绘制的代码。我在自定义视图子类的 drawRect:
中编写了它,但您可以轻松地使用此代码绘制到任何图形上下文中。
Here's the code I wrote to draw that. I wrote it in the drawRect:
of a custom view subclass, but you can easily use this code to draw into any graphics context.
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
首先,我创建一个只是弧形的路径:
First, I create a path that's just an arc:
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
接下来,我要求Core Graphics制作一条新路径,该路径是<$ c $的大纲c> arc 路径。请注意我如何要求它的笔画宽度 kArcThickness
和圆线上限:
Next, I ask Core Graphics to make a new path that is the outline of the arc
path. Note how I ask it for a stroke width of kArcThickness
and round line caps:
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
我还需要该路径的反转:包含的每个点的路径,除了 形状
中的点数。它发生了(尽管我认为没有记录), CGContextCreateCopyByStrokingPath
和 CGPathAddRect
以相反的方向绘制。因此,如果我复制 shape
并在其周围绘制一个巨大的矩形,则非零缠绕规则意味着新路径将与 shape
:
I also need the inverse of that path: a path that includes every point except the points in shape
. It so happens (although I don't think it's documented) that CGContextCreateCopyByStrokingPath
and CGPathAddRect
draw in opposite directions. So if I copy shape
and draw an enormous rectangle around it, the non-zero winding rule means that the new path will be the inverse of shape
:
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
现在我可以开始绘画了。首先,我将用浅灰色填充形状:
Now I can actually start drawing. First, I'll fill in the shape with a light gray color:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
接下来我实际执行上面列出的四个步骤。我必须保存图形状态,以便在完成后撤消剪切和阴影参数。
Next I actually perform the four steps I listed above. I have to save the graphics state so I can undo the clipping and shadow parameters when I'm done.
CGContextSaveGState(gc); {
第1步:剪辑到形状:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
第2步:嗯,我在创建 shapeInverse时已经执行了此步骤/ code>。
Step 2: Well, I did this step already when I created shapeInverse
.
第3步:我设置了阴影参数:
Step 3: I set the shadow parameters:
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
步骤4:我填写第2步的反转形状:
Step 4: I fill the inverse shape from step 2:
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
现在我恢复图形状态,专门恢复剪切路径并取消设置阴影参数。
Now I restore the graphics state, which specifically restores the clipping path and unsets the shadow parameters.
} CGContextRestoreGState(gc);
最后,我将使用 shape
浅灰色使边缘更清晰:
Finally, I'll stroke shape
with a light gray to make the edge crisper:
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
当我完成后我会清理:
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
对于更复杂的形状,你可以看一下我在这里回答和我的答案在这里。
For more complex shapes, you can look at my answer here and my answer here.
以下是所有代码,以便于复制:
Here's all the code together for easy copying:
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
CGContextSaveGState(gc); {
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
} CGContextRestoreGState(gc);
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
这篇关于使用核心图形绘制浮雕弧的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!