Sas*_*ent 36 core-graphics objective-c quartz-2d ios
我需要在Draw应用程序的末尾用箭头绘制线条.我不擅长三角学,所以无法解决这个问题.
用户将手指放在屏幕上并沿任何方向画线.因此,箭头应出现在行尾.
rob*_*off 194
这是一个有趣的小问题.首先,有很多方法可以绘制箭头,弯曲或直边.让我们选择一种非常简单的方法并标记我们需要的测量值:

我们想要编写一个函数来获取起点,终点,尾部宽度,头部宽度和头部长度,并返回一个概述箭头形状的路径.让我们创建一个名为的类别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
Run Code Online (Sandbox Code Playgroud)
由于箭头路径上有七个角,让我们通过命名常量开始实现:
// 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 {
Run Code Online (Sandbox Code Playgroud)
好的,简单的部分就完成了.现在,我们如何在路径上找到这七个点的坐标?如果箭头沿X轴对齐,则更容易找到点:

在轴对齐的箭头上计算点坐标非常容易,但是我们需要箭头的总长度才能完成.我们将使用hypotf标准库中的函数:
CGFloat length = hypotf(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
Run Code Online (Sandbox Code Playgroud)
我们将调用辅助方法来实际计算七个点:
CGPoint points[kArrowPointCount];
[self dqd_getAxisAlignedArrowPoints:points
forLength:length
tailWidth:tailWidth
headWidth:headWidth
headLength:headLength];
Run Code Online (Sandbox Code Playgroud)
但我们需要转换这些点,因为通常我们不会尝试创建轴对齐的箭头.幸运的是,Core Graphics支持一种称为仿射变换的变换,它允许我们旋转和平移(滑动)点.我们将调用另一个辅助方法来创建转换,将我们的轴对齐箭头转换为我们要求的箭头:
CGAffineTransform transform = [self dqd_transformForStartPoint:startPoint
endPoint:endPoint
length:length];
Run Code Online (Sandbox Code Playgroud)
现在我们可以使用轴对齐箭头的点和将其转换为我们想要的箭头的转换来创建核心图形路径:
CGMutablePathRef cgPath = CGPathCreateMutable();
CGPathAddLines(cgPath, &transform, points, sizeof points / sizeof *points);
CGPathCloseSubpath(cgPath);
Run Code Online (Sandbox Code Playgroud)
最后,我们可以换一个UIBezierPath围绕CGPath并返回它:
UIBezierPath *uiPath = [UIBezierPath bezierPathWithCGPath:cgPath];
CGPathRelease(cgPath);
return uiPath;
}
Run Code Online (Sandbox Code Playgroud)
这是计算点坐标的辅助方法.这很简单.如果需要,请参阅轴对齐箭头图.
+ (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);
}
Run Code Online (Sandbox Code Playgroud)
计算仿射变换更复杂.这就是三角进来.你可以使用atan2与CGAffineTransformRotate和CGAffineTransformTranslate函数来创建它,但如果你还记得足够的三角,您可以直接创建它.有关我在此处所做的事情的更多信息,请参阅" Quartz 2D编程指南 "中的"矩阵后面的数学":
+ (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
Run Code Online (Sandbox Code Playgroud)
我把所有的代码放在一个简单的copy'n'paste的要点.
使用此类别,您可以轻松绘制箭头:

由于您只是生成一个路径,您可以选择不填充它,或者不要像在此示例中那样对其进行描边:

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

rob*_*off 15
这是我旧的Objective-C代码的Swift版本.它应该适用于Swift 3.2及更高版本.
extension UIBezierPath {
static func arrow(from start: CGPoint, to end: CGPoint, tailWidth: CGFloat, headWidth: CGFloat, headLength: CGFloat) -> UIBezierPath {
let length = hypot(end.x - start.x, end.y - start.y)
let tailLength = length - headLength
func p(_ x: CGFloat, _ y: CGFloat) -> CGPoint { return CGPoint(x: x, y: y) }
let points: [CGPoint] = [
p(0, tailWidth / 2),
p(tailLength, tailWidth / 2),
p(tailLength, headWidth / 2),
p(length, 0),
p(tailLength, -headWidth / 2),
p(tailLength, -tailWidth / 2),
p(0, -tailWidth / 2)
]
let cosine = (end.x - start.x) / length
let sine = (end.y - start.y) / length
let transform = CGAffineTransform(a: cosine, b: sine, c: -sine, d: cosine, tx: start.x, ty: start.y)
let path = CGMutablePath()
path.addLines(between: points, transform: transform)
path.closeSubpath()
return self.init(cgPath: path)
}
}
Run Code Online (Sandbox Code Playgroud)
以下是您如何调用它的示例:
let arrow = UIBezierPath.arrow(from: CGPoint(x: 50, y: 100), to: CGPoint(x: 200, y: 50),
tailWidth: 10, headWidth: 25, headLength: 40)
Run Code Online (Sandbox Code Playgroud)
小智 7
//This is the integration into the view of the previous exemple
//Attach the following class to your view in the xib file
#import <UIKit/UIKit.h>
@interface Arrow : UIView
@end
#import "Arrow.h"
#import "UIBezierPath+dqd_arrowhead.h"
@implementation Arrow
{
CGPoint startPoint;
CGPoint endPoint;
CGFloat tailWidth;
CGFloat headWidth;
CGFloat headLength;
UIBezierPath *path;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor whiteColor]];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setStroke];
tailWidth = 4;
headWidth = 8;
headLength = 8;
path = [UIBezierPath dqd_bezierPathWithArrowFromPoint:(CGPoint)startPoint
toPoint:(CGPoint)endPoint
tailWidth:(CGFloat)tailWidth
headWidth:(CGFloat)headWidth
headLength:(CGFloat)headLength];
[path setLineWidth:2.0];
[path stroke];
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touchPoint = [touches anyObject];
startPoint = [touchPoint locationInView:self];
endPoint = [touchPoint locationInView:self];
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
endPoint=[touch locationInView:self];
[self setNeedsDisplay];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
endPoint = [touch locationInView:self];
[self setNeedsDisplay];
}
@end
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
23184 次 |
| 最近记录: |