我正在尝试实现一个自定义 slider ,如下图所示。
到目前为止我所做的看起来像这样
请帮我画出这样效果的圆弧。我的代码如下,我正在做的是使用线宽为 kLineWidth 的 CGContextAddArc 绘制圆弧。
- (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 中创建这种效果很容易。
也就是说,使用 Core Graphics 绘制这样的内部阴影需要几个步骤:
- 剪辑到形状(使用例如
CGContextClip
或CGContextClipToMask
)。 - 为形状以外的所有东西制作路径或 mask 。
- 设置阴影参数(使用
CGContextSetShadowWithColor
)。 - 填充步骤 2 中的路径或蒙版。这会在形状内部转换阴影,并且只绘制阴影,因为您在步骤 1 中剪裁到形状。
如果你做对了所有这些,你会得到这样一个不错的结果:
这是我为绘制它而编写的代码。我在自定义 View 子类的 drawRect:
中编写了它,但您可以轻松地使用此代码绘制到任何图形上下文中。
- (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];
接下来,我要求 Core Graphics 创建一条新路径,即 arc
路径的轮廓。请注意我如何要求它的笔画宽度为 kArcThickness
和圆线帽:
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
我还需要该路径的逆:包含shape
中的点的所有点的路径。 CGContextCreateCopyByStrokingPath
和 CGPathAddRect
绘制方向相反(尽管我认为没有记录)就这样发生了。因此,如果我复制 shape
并在其周围画一个巨大的矩形,非零缠绕规则意味着新路径将是 shape
的倒数:
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); {
第 1 步:剪裁形状:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
第 2 步:嗯,我在创建 shapeInverse
时已经执行了这一步。
第 3 步:设置阴影参数:
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
第 4 步:我填充第 2 步中的反向形状:
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
现在我恢复图形状态,具体恢复剪切路径并取消设置阴影参数。
} CGContextRestoreGState(gc);
最后,我将用浅灰色描边 shape
以使边缘更清晰:
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);
}
更复杂的形状可以看my answer here和 my answer here .
为了方便复制,这里把所有的代码放在一起:
- (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);
}
关于iphone - 使用核心图形绘制浮雕弧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10417982/