如何在UIView中绘制贝塞尔曲线

3

我需要在一个 UIView 中制作类似下图中显示的曲线。我必须使用 UIBezierPath。请帮助我。

我还想知道如何将曲线从水平轴翻转,这样我就可以在顶部拥有弧形,并在底部拥有基础。

bezier路径示例


@RahulMishra 我必须使用UIView而不是image。 - Diksha
@RahulMishra 你是在ImageView上使用了Image还是通过BezierPath实现的? - Diksha
我会将我的视图放在地图视图上方,这样它看起来就像曲线下的地图。 - Diksha
@Diksha 如果您能够得到一个有曲线的图像,则制作此 UI 将只需几分钟...只需将 UIView 作为 MapView 和 CurveImageView 的容器即可...将 MapView 帧设置为父视图,然后将 ImageView 放在顶部。 - Rahul
让我们在聊天中继续这个讨论 - Rahul
显示剩余10条评论
1个回答

8
为了在特定的CGSize内绘制一个实心弧形,您可以像这样定义一个UIBezierPath:
- (UIBezierPath * _Nullable)pathOfArcWithinSize:(CGSize)size {
    if (size.width == 0 || size.height <= 0) return nil;

    CGFloat theta = M_PI - atan2(size.width / 2.0, size.height) * 2.0;
    CGFloat radius = self.bounds.size.height / (1.0 - cos(theta));

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, 0)];
    [path addArcWithCenter:CGPointMake(size.width / 2.0, -radius + size.height) radius:radius startAngle:M_PI_2 + theta endAngle:M_PI_2 - theta clockwise:false];
    [path closePath];

    return path;
}

这只是使用一点三角函数来计算弧形的角度和半径,给定视图的高度和宽度。

有了这个,您可以构建一个CAShapeLayer使用该路径,然后将其作为UIView的子层添加,或者您可以实现自己的drawRect方法,调用该路径上的fill。(或者,考虑到您已经使用进行标记,您也可以使用CoreGraphics调用进行自定义的drawRect,但我不确定为什么要这样做。)

例如,您可以定义一个CurvedView类,该类使用CAShapeLayer

//  CurvedView.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface CurvedView : UIView

@property (nonatomic, strong) IBInspectable UIColor *fillColor;

@end

//  CurvedView.m

#import "CurvedView.h"

@interface CurvedView ()
@property (nonatomic, weak) CAShapeLayer *curvedLayer;
@end

@implementation CurvedView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self configureView];
    }
    return self;
}

- (instancetype _Nullable)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        [self configureView];
    }
    return self;
}

- (void)configureView {
    self.fillColor = [UIColor whiteColor];

    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.fillColor = self.fillColor.CGColor;
    layer.strokeColor = [UIColor clearColor].CGColor;
    layer.lineWidth = 0;
    [self.layer addSublayer:layer];
    self.curvedLayer = layer;
}

- (void)setFillColor:(UIColor *)fillColor {
    _fillColor = fillColor;

    self.curvedLayer.fillColor = fillColor.CGColor;
}

- (void)layoutSubviews {
    [super layoutSubviews];

    self.curvedLayer.path = [self pathOfArcWithinSize:self.bounds.size].CGPath;
}

- (UIBezierPath * _Nullable)pathOfArcWithinSize:(CGSize)size {
    if (size.width == 0 || size.height <= 0) return nil;

    CGFloat theta = M_PI - atan2(size.width / 2.0, size.height) * 2.0;
    CGFloat radius = self.bounds.size.height / (1.0 - cos(theta));

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, 0)];
    [path addArcWithCenter:CGPointMake(size.width / 2.0, -radius + size.height) radius:radius startAngle:M_PI_2 + theta endAngle:M_PI_2 - theta clockwise:false];
    [path closePath];

    return path;
}

@end

那会产生:

enter image description here

或者,如果您更愿意使用drawRect方法而不是使用CAShapeLayer

//  CurvedView.m

#import "CurvedView.h"

@implementation CurvedView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self configureView];
    }
    return self;
}

- (instancetype _Nullable)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        [self configureView];
    }
    return self;
}

- (void)configureView {
    self.fillColor = [UIColor whiteColor];
}

- (void)setFillColor:(UIColor *)fillColor {
    _fillColor = fillColor;

    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    UIBezierPath *path = [self pathOfArcWithinSize:self.bounds.size];
    [self.fillColor setFill];
    [path fill];
}

- (UIBezierPath * _Nullable)pathOfArcWithinSize:(CGSize)size {
    if (size.width == 0 || size.height <= 0) return nil;

    CGFloat theta = M_PI - atan2(size.width / 2.0, size.height) * 2.0;
    CGFloat radius = self.bounds.size.height / (1.0 - cos(theta));

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, 0)];
    [path addArcWithCenter:CGPointMake(size.width / 2.0, -radius + size.height) radius:radius startAngle:M_PI_2 + theta endAngle:M_PI_2 - theta clockwise:false];
    [path closePath];

    return path;
}

@end

如果你想让弧形占据视图的底部,路径看起来应该像这样:
- (UIBezierPath * _Nullable)pathOfArcWithinSize:(CGSize)size {
    if (size.width == 0 || size.height <= 0) return nil;

    CGFloat theta = M_PI - atan2(size.width / 2.0, size.height) * 2.0;
    CGFloat radius = self.bounds.size.height / (1.0 - cos(theta));

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, size.height)];
    [path addArcWithCenter:CGPointMake(size.width / 2.0, radius) radius:radius startAngle:M_PI_2 * 3.0 + theta endAngle:M_PI_2 * 3.0 - theta clockwise:false];
    [path closePath];

    return path;
}

基本上,这是相同的thetaradius,但从左下角开始,将center设置为size.width / 2.0,radius,并从M_PI_2 * 3.0 ± theta 弧度:

enter image description here


谢谢Rob,你再次帮了我一个大忙。我想学习这个,但实际上不知道如何处理UIBeizers,所以你能告诉我从哪里开始学习吗?谢谢 :) - Diksha
@Elan - 关于贝塞尔路径的基础知识,请参阅苹果文档(https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/BezierPaths/BezierPaths.html#//apple_ref/doc/uid/TP40010156-CH11-SW1)或搜索“uibezierpath教程”。要实现所需路径的数学计算,需要借助我从时光中迷雾中挖掘出来的高中三角学知识。 :) 最重要的是,只要开始尝试在自定义的UIView子类(如上所述)或CAShapeLayer中构建自己的UIBezierPath,相信你很快就能掌握它。这并不像看起来那么难。 - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接