如何使用Core Graphics绘制箭头? [英] How can I draw an arrow using Core Graphics?

查看:145
本文介绍了如何使用Core Graphics绘制箭头?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在Draw应用程序的末尾用箭头绘制线条。我不擅长三角学,所以无法解决这个问题。



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

解决方案

更新



中的noreferrer>矩阵背后的数学,了解我在这里做的更多信息:

  +(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

我把所有的轻松复制''的要点



使用此类别,您可以轻松绘制箭头:





由于你只是生成一个路径,你可以选择不填充它,或者不像这个例子那样抚摸它:





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




I need to draw line with arrow on its end in my Draw app. I'm not good in trigonometry, so can't solve this problem.

The user put his finger on the screen and draw the line in any direction. So, the arrow should appear on the line end.

解决方案

UPDATE

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

ORIGINAL

This is a fun little problem. First of all, there are lots of ways to draw arrows, with curved or straight sides. Let's pick a very simple way and label the measurements we'll need:

We want to write a function that takes the start point, the end point, the tail width, the head width, and the head length, and returns a path outlining the arrow shape. Let's create a category named dqd_arrowhead to add this method to 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

Since there are seven corners on the path of the arrow, let's start our implementation by naming that constant:

// 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 {

OK, the easy part is done. Now, how do we find the coordinates of those seven points on the path? It is much easier to find the points if the arrow is aligned along the X axis:

It's pretty easy to compute the point coordinates on an axis-aligned arrow, but we'll need the overall length of the arrow to do it. We'll use the hypotf function from the standard library:

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

We'll call on a helper method to actually compute the seven points:

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

But we need to transform those points, because in general we're not trying to create an axis-aligned arrow. Fortunately, Core Graphics supports a kind of transformation called an affine transformation, which lets us rotate and translate (slide) points. We'll call another helper method to create the transform that turns our axis-aligned arrow into the arrow we were asked for:

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

Now we can create a Core Graphics path using the points of the axis-aligned arrow and the transform that turns it into the arrow we want:

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

Finally, we can wrap a UIBezierPath around the CGPath and return it:

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

Here's the helper method that computes the point coordinates. It's quite simple. Refer back to the diagram of the axis-aligned arrow if you need to.

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

Computing the affine transform is more complicated. This is where the trigonometry comes in. You could use atan2 and the CGAffineTransformRotate and CGAffineTransformTranslate functions to create it, but if you remember enough trigonometry, you can create it directly. Consult "The Math Behind the Matrices" in the Quartz 2D Programming Guide for more information about what I'm doing here:

+ (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

I have put all of the code in a gist for easy copy'n'paste.

With this category, you can easily draw arrows:

Since you're just generating a path, you can choose not to fill it, or not to stroke it as in this example:

You have to be careful, though. This code doesn't prevent you from getting funky results if you make the head width less than the tail width, or if you make the head length larger than the total arrow length:

这篇关于如何使用Core Graphics绘制箭头?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆