使用ObjectiveC的Core Graphics,如何围绕圆圈弯曲箭头?

Ken*_*rey 5 curve core-graphics objective-c drawrect ios

我们的设计师让我重新创建: 一个围绕圆圈旋转的橙色箭头条

我是UIView的子类,我已经覆盖了drawRect命令,如下所示:

[super drawRect:frame];

CGFloat x = self.frame.origin.x;
CGFloat y = self.frame.origin.y;
CGFloat w = self.frame.size.width;
CGFloat h = self.frame.size.height;
CGFloat lineWidth = lineWidthRequested;
CGPoint centerPoint = CGPointMake(w/2, h/2);
CGFloat radius = radiusRequested;
CGFloat startAngle = 3 * M_PI / 2;
CGFloat endAngle = startAngle + percentage * 2 * M_PI;

CGMutablePathRef arc = CGPathCreateMutable();
CGPathAddArc(arc, NULL,
             centerPoint.x, centerPoint.y,
             radius,
             startAngle,
             endAngle,
             NO);
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(arc, NULL,
                               lineWidth,
                               kCGLineCapButt,
                               kCGLineJoinMiter, // the default
                               10); // 10 is default miter limit

CGContextRef c = UIGraphicsGetCurrentContext();
CGContextAddPath(c, strokedArc);
CGContextSetFillColorWithColor(c, [UIColor colorWithRed:239/255.0 green:101/255.0 blue:47/255.0 alpha:1.0].CGColor);
CGContextDrawPath(c, kCGPathFill);
Run Code Online (Sandbox Code Playgroud)

我最终得到的是: 圆圈周围的橙色条

还得画箭头.会很容易,对吧?

在努力记住我的触发后,我发现在这个页面的中心周围旋转点: 在VB中旋转原点周围的点

但是当我尝试翻译到目标C来绘制箭头时,我得到了非常奇怪的结果.这是drawRect中的代码:

    CGFloat triangle[3][2] = {{centerPoint.x + 10, h - (centerPoint.y + radius)},
    {centerPoint.x, h - (centerPoint.y + radius + lineWidth/2)},
    {centerPoint.x, h - (centerPoint.y + radius - lineWidth/2)}};

for (int idx=0;idx < 3; idx++) {
    // translate to origin
    triangle[idx][0] -= centerPoint.x;
    triangle[idx][1] -= centerPoint.y;
}

CGFloat angDistance = endAngle - startAngle;
CGFloat ct = cos(angDistance);
CGFloat st = sin(angDistance);

for (int idx=0;idx < 3; idx++) {
    // rotate
    triangle[idx][0] = ct * triangle[idx][0] - st * triangle[idx][1];
    triangle[idx][1] = -st * triangle[idx][0] + ct * triangle[idx][1];
}

for (int idx=0;idx < 3; idx++) {
    // translate back to position
    triangle[idx][0] += centerPoint.x;
    triangle[idx][1] += centerPoint.y;
}

NSLog(@"Rotating through %g, %06.1f,%06.1f  , ct - %g, st - %g",angDistance, triangle[0][0],triangle[0][1],ct, st);

// XXX todo draw the filled triangle at end.
// draw a red triangle, the point of the arrow
CGContextSetFillColorWithColor(c, [[UIColor greenColor] CGColor]);
CGContextMoveToPoint(c, triangle[0][0], triangle[0][1]);
CGContextAddLineToPoint(c, triangle[1][0], triangle[1][1]);
CGContextAddLineToPoint(c, triangle[2][0], triangle[2][1]);
CGContextFillPath(c);
Run Code Online (Sandbox Code Playgroud)

我期待我制作这些点,然后将它们翻译成原点,旋转,然后将它们翻译回来,我会笑.

然而,这并不是正在发生的事情......当百分比从0增加到2pi时,箭头会在一个模糊的三角形路线中自行绘制.当angDistance为零或pi时,箭头位于正确的位置.当我朝向pi/2或3pi/2时,箭头朝向封闭矩形的下角移开.

我必须做一些明显愚蠢的事情,但我不能为我的生活看到它.

有任何想法吗?

谢谢,

-Ken

Rob*_*Rob 5

我建议为所需形状的整个轮廓构建一条路径,然后用所需的颜色"填充"该路径.这消除了任何间隙或任何不完整排队的风险.

因此,该路径可能包括箭头外部的弧,箭头的两条线,箭头内部的弧形,然后关闭路径.这可能看起来像:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, self.arrowColor.CGColor);

    CGPoint center = CGPointMake(rect.size.width / 2.0, rect.size.height / 2.0);

    CGContextMoveToPoint(context, center.x + cosf(self.startAngle) * (self.radius + self.lineWidth / 2.0),
                                  center.y + sinf(self.startAngle) * (self.radius + self.lineWidth / 2.0));

    CGContextAddArc(context, center.x, center.y, self.radius + self.lineWidth / 2.0, self.startAngle, self.endAngle, !self.clockwise);

    CGFloat theta = asinf(self.lineWidth / self.radius / 2.0) * (self.clockwise ? 1.0 : -1.0);
    CGFloat pointDistance = self.radius / cosf(theta);

    CGContextAddLineToPoint(context, center.x + cosf(self.endAngle + theta) * pointDistance,
                                     center.y + sinf(self.endAngle + theta) * pointDistance);

    CGContextAddLineToPoint(context, center.x + cosf(self.endAngle) * (self.radius - self.lineWidth / 2.0),
                                     center.y + sinf(self.endAngle) * (self.radius - self.lineWidth / 2.0));

    CGContextAddArc(context, center.x, center.y, self.radius - self.lineWidth / 2.0, self.endAngle, self.startAngle, self.clockwise);

    CGContextClosePath(context);

    CGContextDrawPath(context, kCGPathFill);
}
Run Code Online (Sandbox Code Playgroud)

这里唯一的技巧就是为箭头结束提出正确的观点.我已经改进了更好地处理更胖箭头的选择,但是您应该随意使用您认为最适合您应用的任何东西.

因此,以下代码:

self.arrowView.radius = 100;
self.arrowView.arrowColor = [UIColor blueColor];
self.arrowView.lineWidth = 40;
self.arrowView.startAngle = -M_PI_2;
self.arrowView.endAngle = M_PI;
self.arrowView.clockwise = TRUE;
Run Code Online (Sandbox Code Playgroud)

会得到以下(我用a制作动画CADisplayLink):

箭头

这使用零起​​始角作为"3点钟"位置,但你可以在你认为合适时调整它.但希望它说明了解决问题的一种方法.

顺便说一句,虽然我已经回答了如何使用CoreGraphics这样做的问题,但我不一定建议这样做.例如,在https://github.com/robertmryan/CircularArrowDemo中,我没有实现drawRect,而是更新了CAShapeLayer.通过这样做,我不仅可以避免drawRect效率低下,而且理论上也可以改变你使用它的方式CAShapeLayer(例如,将它用作mask某些人UIView,在其背后显示一些更有趣的颜色渐变(或其他图像)).