gpt4 book ai didi

objective-c - 如何使用 Core Graphics 绘制箭头?

转载 作者:IT老高 更新时间:2023-10-28 11:48:26 26 4
gpt4 key购买 nike

我需要在我的 Draw 应用程序中用箭头画线。我三角学不好,所以解决不了这个问题。

用户将手指放在屏幕上并向任意方向画线。所以,箭头应该出现在行尾。

最佳答案

更新

I've posted a Swift version of this answer separately.

原创

这是一个有趣的小问题。首先,有很多方法来绘制箭头,有弯曲的或直的。让我们选择一种非常简单的方法并标记我们需要的测量值:

arrow parts

我们想写一个函数,它接受起点、终点、尾部宽度、头部宽度和头部长度,并返回一个勾勒出箭头形状的路径。让我们创建一个名为 dqd_arrowhead 的类别来将此方法添加到 UIBezierPath:

// UIBezierPath+dqd_arrowhead.h

@interface UIBezierPath (dqd_arrowhead)

+ (UIBezierPath *)dqd_bezierPathWithArrowFromPoint:(CGPoint)startPoint
toPoint:(CGPoint)endPoint
tailWidth:(CGFloat)tailWidth
headWidth:(CGFloat)headWidth
headLength:(CGFloat)headLength;

@end

由于箭头的路径上有七个角,让我们通过命名该常量来开始我们的实现:

// UIBezierPath+dqd_arrowhead.m

#import "UIBezierPath+dqd_arrowhead.h"

#define kArrowPointCount 7

@implementation UIBezierPath (dqd_arrowhead)

+ (UIBezierPath *)dqd_bezierPathWithArrowFromPoint:(CGPoint)startPoint
toPoint:(CGPoint)endPoint
tailWidth:(CGFloat)tailWidth
headWidth:(CGFloat)headWidth
headLength:(CGFloat)headLength {

好的,简单的部分完成了。现在,我们如何找到路径上这七个点的坐标?如果箭头沿 X 轴对齐,则更容易找到点:

axis-aligned arrow points

计算轴对齐箭头上的点坐标非常容易,但我们需要箭头的总长度来计算。我们将使用标准库中的 hypotf 函数:

    CGFloat length = hypotf(endPoint.x - startPoint.x, endPoint.y - startPoint.y);

我们将调用一个辅助方法来实际计算这七个点:

    CGPoint points[kArrowPointCount];
[self dqd_getAxisAlignedArrowPoints:points
forLength:length
tailWidth:tailWidth
headWidth:headWidth
headLength:headLength];

但我们需要转换这些点,因为通常我们试图创建一个轴对齐的箭头。幸运的是,Core Graphics 支持一种称为仿射变换的变换,它可以让我们旋转和平移(滑动)点。我们将调用另一个辅助方法来创建将轴对齐的箭头转换为我们要求的箭头的转换:

    CGAffineTransform transform = [self dqd_transformForStartPoint:startPoint
endPoint:endPoint
length:length];

现在我们可以使用轴对齐箭头的点和将其变成我们想要的箭头的变换来创建 Core Graphics 路径:

    CGMutablePathRef cgPath = CGPathCreateMutable();
CGPathAddLines(cgPath, &transform, points, sizeof points / sizeof *points);
CGPathCloseSubpath(cgPath);

最后,我们可以在 CGPath 周围包裹一个 UIBezierPath 并返回它:

    UIBezierPath *uiPath = [UIBezierPath bezierPathWithCGPath:cgPath];
CGPathRelease(cgPath);
return uiPath;
}

这是计算点坐标的辅助方法。这很简单。如果需要,请引用轴对齐箭头的图表。

+ (void)dqd_getAxisAlignedArrowPoints:(CGPoint[kArrowPointCount])points
forLength:(CGFloat)length
tailWidth:(CGFloat)tailWidth
headWidth:(CGFloat)headWidth
headLength:(CGFloat)headLength {
CGFloat tailLength = length - headLength;
points[0] = CGPointMake(0, tailWidth / 2);
points[1] = CGPointMake(tailLength, tailWidth / 2);
points[2] = CGPointMake(tailLength, headWidth / 2);
points[3] = CGPointMake(length, 0);
points[4] = CGPointMake(tailLength, -headWidth / 2);
points[5] = CGPointMake(tailLength, -tailWidth / 2);
points[6] = CGPointMake(0, -tailWidth / 2);
}

计算仿射变换更复杂。这就是三角函数的用武之地。您可以使用 atan2CGAffineTransformRotateCGAffineTransformTranslate 函数来创建它,但如果你记得足够多的三角函数,您可以直接创建它。咨询“The Math Behind the Matrices” in the Quartz 2D Programming Guide有关我在这里做什么的更多信息:

+ (CGAffineTransform)dqd_transformForStartPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint
length:(CGFloat)length {
CGFloat cosine = (endPoint.x - startPoint.x) / length;
CGFloat sine = (endPoint.y - startPoint.y) / length;
return (CGAffineTransform){ cosine, sine, -sine, cosine, startPoint.x, startPoint.y };
}

@end

我已将所有代码放入 a gist for easy copy'n'paste .

有了这个分类,你就可以轻松画箭头了:

sample arrow 1 sample arrow 2

由于您只是生成一条路径,因此您可以选择不填充它,或者像本例中那样不描边:

unstroked arrow sample

不过,你必须小心。如果您使头部宽度小于尾部宽度,或者头部长度大于总箭头长度,则此代码不会阻止您获得时髦的结果:

narrow head sample head too long sample

关于objective-c - 如何使用 Core Graphics 绘制箭头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13528898/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com