How to Draw a Bezier Curve in UIView
Asked Answered
A

1

3

I need to make a curve in a UIView as displayed in the image below. I have to use UIBezierPath. Kindly help me out with this.

I also want to know how to flip the curve from the horizontal axis, so that I have the arc at the top and the base at the bottom.

Example of bezier path

Aulic answered 14/6, 2016 at 7:8 Comment(15)
Refer this link raywenderlich.com/34003/…Lopsided
I think this link is useful to you #12905267Riding
is their any restriction on using Image as curve ? @AulicInnovation
@RahulMishra i have to use UIView rather than imageAulic
@Aulic You can Use a UIView and Put an UIImageView inside it..and Place a image having curve. I have done same functionalityInnovation
@RahulMishra You have used Image on imageview or you have done through beizerpathAulic
@Aulic I have taken a white Image having same curve and Applied to ImageView and then Positioned the UIImage VIew accordingly...Innovation
@Aulic Do you have this white Curve Image ?Innovation
@RahulMishra no i have to execute the task with beizerpathAulic
@Aulic I haven't used Bezire Curve. I will try if I get this curve then will post my answerInnovation
@Aulic Even If you make A curve then How can You acheive same UI as shown in Image.... Bezire curve will only add a curve. How will you cut the map part under the curve.Innovation
I will put my view over the mapview thus it will look like the map under the curveAulic
@Aulic If you can manage to get a Image have curve then it will takes minutes to make this UI.... Just Make A UIVIew as a container of MapView and CurveImageView....Make MapView frame equal to Parent View and at the top put your ImageView..Innovation
@RahulMishra No rahul,actually i have to do it without image.I know it would be easy with imageAulic
Let us continue this discussion in chat.Innovation
G
8

To draw a solid filled in arc within a particular CGSize, you can define a UIBezierPath like so:

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

That's just using a little trigonometry to calculate the angle and radius for the arc given the height and width of the view.

Once you have that, you can either construct a CAShapeLayer using that path and then add that as a sublayer of a UIView or you can implement your own drawRect method that calls fill on that path. (Or, given that you've tagged this with , you could also do a custom drawRect with CoreGraphics calls, but I'm not sure why you'd do that.)

For example, you could define a CurvedView class that uses CAShapeLayer:

//  CurvedView.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface CurvedView : UIView

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

@end

And

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

That yields:

enter image description here

Or, if you'd rather use the drawRect approach rather than using 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

If you want the arc to occupy the bottom of the view, the path would look like:

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

Essentially, that's the same theta and radius, but start in lower left corner, set the center to be size.width / 2.0, radius, and arc from M_PI_2 * 3.0 ± theta:

enter image description here

Gantline answered 14/6, 2016 at 17:2 Comment(2)
Thanks rob..you saved me again..i want to learn this..actually dont know how to deal with UIBeizers,i want to learn this,so can you help me from where can i start...thanks...:)Aulic
@Elan - For the basics of bezier paths, see Apple documentation (developer.apple.com/library/ios/documentation/2DDrawing/…) or google "uibezierpath tutorial". For the maths to achieve the desired path, this is drawing on my old high-school trigonometry from the mists of time. :) Bottom line, just start playing around with building your own UIBezierPath in either custom UIView subclass (like above) or CAShapeLayer and I'm sure you'll pick it up quickly. It's not as hard as it looks.Gantline

© 2022 - 2024 — McMap. All rights reserved.