8

我正在编写一个 iPad 应用程序,我在其中将表示形状的 XML 对象呈现为屏幕上的图形。我试图渲染的对象之一是弧。本质上,这些弧线为我提供了一个边界矩形以及一个开始和结束角度。

给定属性:

  • X
  • 是的
  • 宽度
  • 高度
  • 起始角
  • 结束角

使用这些值,我需要绘制圆弧(本质上是椭圆的一部分)。我不能使用以下内容:

    UIBezierPath *arc = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x, y, width, height)];
    [UIColor blackColor] setStroke];
    [arc stroke];

因为它画了一个完整的椭圆。基本上我需要上面的,但它需要考虑开始和结束角度,所以只显示椭圆的一部分。我认为这将涉及绘制三次贝塞尔曲线或二次贝塞尔曲线。问题是我不知道如何使用给定的信息计算起点、终点或控制点。

4

3 回答 3

10

您可以通过围绕椭圆的绘图设置剪辑路径来实现您想要的。

CGContextSaveGState(theCGContext);
CGPoint center = CGPointMake(x + width / 2.0, y + height / 2.0);
UIBezierPath* clip = [UIBezierPath bezierPathWithArcCenter:center
                                                    radius:max(width, height)
                                                startAngle:startAngle
                                                  endAngle:endAngle
                                                 clockwise:YES];
[clip addLineToPoint:center];
[clip closePath];
[clip addClip];

UIBezierPath *arc = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x, y, width, height)];
[[UIColor blackColor] setStroke];
[arc stroke];

CGContextRestoreGState(theCGContext);

剪裁的确切半径并不重要。它需要足够大,以便它只在末端剪辑椭圆,而不是通过所需的弧。

于 2012-05-17T22:05:38.170 回答
4

这个类别会有所帮助。

#import <Foundation/Foundation.h>

@interface UIBezierPath (OvalSegment)

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle;
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle angleStep:(CGFloat)angleStep;

@end

#import "UIBezierPath+OvalSegment.h"

@implementation UIBezierPath (OvalSegment)

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle angleStep:(CGFloat)angleStep {
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    CGFloat xRadius = CGRectGetWidth(rect)/2.0f;
    CGFloat yRadius = CGRectGetHeight(rect)/2.0f;

    UIBezierPath *ellipseSegment = [UIBezierPath new];

    CGPoint firstEllipsePoint = [self ellipsePointForAngle:startAngle withCenter:center xRadius:xRadius yRadius:yRadius];
    [ellipseSegment moveToPoint:firstEllipsePoint];

    for (CGFloat angle = startAngle + angleStep; angle < endAngle; angle += angleStep) {
        CGPoint ellipsePoint = [self ellipsePointForAngle:angle withCenter:center xRadius:xRadius yRadius:yRadius];
        [ellipseSegment addLineToPoint:ellipsePoint];
    }

    CGPoint lastEllipsePoint = [self ellipsePointForAngle:endAngle withCenter:center xRadius:xRadius yRadius:yRadius];
    [ellipseSegment addLineToPoint:lastEllipsePoint];

    return ellipseSegment;
}

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle {
    return [UIBezierPath bezierPathWithOvalInRect:rect startAngle:startAngle endAngle:endAngle angleStep:M_PI/20.0f];
}

+ (CGPoint)ellipsePointForAngle:(CGFloat)angle withCenter:(CGPoint)center xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius {
    CGFloat x = center.x + xRadius * cosf(angle);
    CGFloat y = center.y - yRadius * sinf(angle);
    return CGPointMake(x, y);
}

@end
于 2015-05-13T20:06:54.683 回答
2

您将不得不使用addQuadCurveToPoint:controlPoint:而不是bezierPathWithOvalInRect.

方法定义如下:-

- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint

也就是说 CGPoint 是两个参数的参数(输入)。

moveToPoint:您还必须在调用之前设置起点addQuadCurveToPoint,这将作为当前点(您可以在方法中看到他们的没有起点作为参数)。

在您的情况下,您将拥有

1>x,y 作为你的起点

2>x+width 和 y+height 作为端点

您在这里不需要角度,或者您可以实现您的逻辑来使用角度。我正在上传图像以使事情变得清晰。

这是描述起点、终点和控制点的图像

于 2012-05-17T21:57:46.517 回答