如何使用Quartz 2D/Core Graphics画一个具有圆角的三角形

7
我想画一个像这样的三角形: rounded triangle 目前我正在使用 CAShapeLayer 和 UIBezierPath 来创建路径(代码如下),然后将其应用为另一层的掩码(我在 UIView 子类中使用 self.layer 而不是设置 layerclass ,以便保留初始层)。
以下是代码:
_bezierPath = [[UIBezierPath bezierPath] retain];
#define COS30 0.86602540378
#define SIN30 0.5
[_bezierPath moveToPoint:(CGPoint){self.frame.size.width/2.f-r*SIN30,r*COS30}];
[_bezierPath addArcWithCenter:(CGPoint){self.frame.size.width/2.f,r*COS30*2.f} radius:r startAngle:2*M_PI/3.f endAngle:M_PI/3.f clockwise:YES];
[_bezierPath addLineToPoint:(CGPoint){self.frame.size.width-r*SIN30,self.frame.size.height-r*COS30}];
[_bezierPath addArcWithCenter:(CGPoint){self.frame.size.width-r*SIN30-r,self.frame.size.height-r*COS30} radius:r startAngle:0.f endAngle:-M_PI/3.f clockwise:YES];
[_bezierPath addLineToPoint:(CGPoint){r*SIN30,self.frame.size.height-r*COS30}];
[_bezierPath addArcWithCenter:(CGPoint){r*SIN30+r,self.frame.size.height-r*COS30} radius:r startAngle:4*M_PI/3.f endAngle:M_PI clockwise:YES];
[_bezierPath closePath];
CAShapeLayer *s = [CAShapeLayer layer];
s.frame = self.bounds;
s.path = _bezierPath.CGPath;
self.layer.mask = s;
self.layer.backgroundColor = [SLInsetButton backgroundColorForVariant:SLInsetButtonColorForSLGamePieceColor(_color)].CGColor;

很不幸,结果并非我所期望的。相反,角落变成了小旋钮(好像转动了太多)。谢谢!

为什么使用cos(30)而不是60? - AlexWien
你指的是哪种情况? - DanZimm
1个回答

14

一位聪明的朋友(如果你遇到他的话,名叫约翰·希顿,他非常棒)提醒我核心图形库使用的lineCap和lineJoin属性。

某处:

#define border (10.f)

并且在你的界面中:

@property (nonatomic, strong) UIBezierPath *bezierPath;

在你的初始化函数中,创建一个类似这样的短路径:

CGFloat inset = border / 2.f;
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:(CGPoint){ self.frame.size.width/2.f, inset }];
[bezierPath addLineToPoint:(CGPoint){ self.frame.size.width - inset, self.frame.size.height - inset }];
[bezierPath addLineToPoint:(CGPoint){ a, self.frame.size.height - a }];
[bezierPath closePath];

self.bezierPath = bezierPath;

然后在您的drawRect:方法中,执行以下操作:

CGContextRef c = UIGraphicsGetCurrentContext(), context = c;
CGColorRef col = [UIColor redColor].CGColor;
CGColorRef bcol = [UIColor redColor].CGColor;
CGContextSetFillColorWithColor(c, col);
CGContextSetStrokeColorWithColor(c, bcol);
CGContextSetLineWidth(c, border);
CGContextSetLineJoin(c, kCGLineJoinRound);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextAddPath(c, self.bezierPath.CGPath);
CGContextStrokePath(c);
CGContextAddPath(c, self.bezierPath.CGPath);
CGContextFillPath(c);

这样可以正确地在三角形上实现圆角,还可以选择是否添加边框或轮廓线。


考虑用const替换#define - Iulian Onofrei
我选择使用宏而不是像static const这样的东西,因为你永远不需要通过指针引用数字,额外的类型检查在这里是不必要的(它在一个假定它是浮点数的例程中使用),并且它打开了在编译时提供此常量的能力,无论是使用cli args还是在xcode的配置中(当然,名称“border”不是最好的,应该更改,但是这段代码不应该简单地复制/粘贴,而应该仔细阅读和思考)。 - DanZimm
我理解,但是如果宏被更改,整个项目不是都需要重新编译吗? - Iulian Onofrei
说实话,我不确定它是否重编译整个项目或仅重编译必要的文件-当然,确保只有该文件被重新编译的一种方法是更改该文件上的标志。无论如何,从我的角度来看,在我使用宏而不是“静态常量”的情况下,似乎没有任何好处或伤害。 - DanZimm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接