繁体   English   中英

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

[英]Using Core Graphics from ObjectiveC, how to you curve an arrow around a circle?

我们的设计师要求我重新创建此代码: 橙色的箭头栏绕圆旋转

我将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);

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

仍然要画箭头。 会很容易吧?

在努力记住我的触发器之后,我在此页面上发现了围绕中心旋转的点在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);

我原以为我会提出这些要点,然后将其转换为原点,旋转,然后再将其转换回去,这很有趣。

但是,事实并非如此...随着百分比从0增加到2pi,箭头将自己绘制成模糊的三角形路线。 当angDistance为零或pi时,箭头在正确的位置。 当我朝pi / 2或3pi / 2前进时,箭头朝封闭矩形的下角移动。

我一定在做些公然愚蠢的事,但我一生都看不到。

有任何想法吗?

谢谢,

-肯

我建议为所需形状的整个轮廓构建路径,然后用所需颜色“填充”该路径。 这样就消除了任何缝隙或排列不齐的风险。

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

- (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);
}

这里唯一的技巧是为箭头的末端找到正确的点。 我已经改善了处理粗箭头的选择,但是您应该随意使用最适合自己的应用的东西。

因此,以下代码:

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;

将产生以下内容(我正在用CADisplayLink动画):

箭头

它将起始角度零表示为“ 3点钟”位置,但是您可以根据自己的需要对它进行微调。 但希望它能说明解决此问题的一种方法。

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

这是另一种解决方案(虽然不是很可扩展)。 该解决方案假定这就像一个徽标,其中绘制的角度/圆百分比不会改变。

- (void)drawRect:(CGRect)rect {
    UIBezierPath *circleOutline = [UIBezierPath bezierPath];

    [self.circleColor setStroke];
    [circleOutline setLineWidth:self.bounds.size.width*0.15];
    [circleOutline addArcWithCenter:CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2) radius:self.bounds.size.width/2-circleOutline.lineWidth/2 startAngle:3*M_PI/2 endAngle:3*M_PI/4 clockwise:YES];
    [circleOutline stroke];

    [self addArrowView:circleOutline];
}

- (void)addArrowView:(UIBezierPath *)path {
    for (int x = 0; x < self.bounds.size.width/2; x++) {
        for (int y = self.bounds.size.height/2; y < self.bounds.size.height; y++) {
            if ([path containsPoint:CGPointMake(x, y)]) {

                // Pythagorean Theorem - We want the diagonal length of the square to be lineWidth, so we need to calculate what size
                // to make each side of the square to make the diagonal equal to lineWidth
                double sideLength = sqrt((path.lineWidth*path.lineWidth)/2);

                UIView *arrowView = [[UIView alloc] initWithFrame:CGRectMake(x-sideLength/2, y-sideLength/2, sideLength, sideLength)];
                arrowView.backgroundColor = self.circleColor;
                [self addSubview:arrowView];

                return;
            }
        }
    }
}

将产生:

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM