A comprehensive introduction to UIBezierPath

Posted by akiratoya13 on Fri, 28 Jun 2019 21:02:23 +0200

UIBezierPath in UIKit is an encapsulation of the Core Graphics framework about path. Vector-based paths can be created, such as ellipses or rectangles, or shapes with multiple lines and curves.

Basic Introduction and Use of Core Graphics

Attributes:

  • lineWidth: The line width attribute defines the curve specification drawn in the UIBezierPath object. The default is: 1.0
  • lineCapStyle: Applied to the endpoint and starting point of a curve. This property is ineffective in a closed subpath. By default: kCGLineCapButt

Enumeration values:

  typedef CF_ENUM(int32_t, CGLineCap) {
    kCGLineCapButt,
    kCGLineCapRound,
    kCGLineCapSquare
};

Corresponding styles:


  • LineJoin Style: The style of the curve joints.

Enumeration values:

  typedef CF_ENUM(int32_t, CGLineJoin) {
    kCGLineJoinMiter,
    kCGLineJoinRound,
    kCGLineJoinBevel
};

Corresponding styles:


  • Miter Limit: The maximum distance between the inner and outer corners at the intersection of two lines, only when the connection point style is kCGLine Join Miter.
Illustration:

  • flatness: Rendering accuracy (the maximum allowable distance between the point of the real curve and the point of the rendering curve). The smaller the value, the higher the accuracy. default: 0.6.
  • usesEvenOddFillRule: Whether to use base-pair filling rules or not. Detailed Introduction to Two Rules
 If set to YES, the path will be filled with even-odd rule.
 If set to NO, the path will be filled with non-zero rules.
  • CGPath: An immutable CGPathRef object
He can pass in the function provided by CoreGraphics. You can create a path using the method provided by the CoreGraphics framework and assign a value to this property. When a new path is set, the path object you give will be copied.
  • CurrtPoint: The starting point of the next drawn line or curve. If the current path is empty, the value of this property will be CGPointZero
  • bounds: a rectangular area covered by a path. This property describes a minimal rectangular region that can contain all the points in the path. The region contains the control points of the quadratic Bessel curve and the cubic Bessel curve.
  • Empty: Is the path empty? <Note>: Even if you only call the moveToPoint method, the current path is considered not empty.

Create instance objects:

  • Create
+ (instancetype) bezierPath;
  • by rectangle
/**
  * This method will create a closed path, starting with the origin of rect parameter, and adding a line clockwise to form a rectangle.
  * @param rect:   Frame of Rectangular Path
  */
+ (instancetype)bezierPathWithRect:(CGRect)rect;
  • by ellipse
/**
  * This method will create a closed path, which will draw a Bezier curve clockwise to draw an approximate ellipse shape. If the rect parameter specifies a rectangle, the UIBezierPath object will describe a circle.
  * @param rect:   Frame of Rectangular Path
  */
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
  • by rounded rectangle:
/**
  * This method will create a closed path, which will draw straight lines and curves clockwise. When rect is square and cornerRadius is equal to half the side length, the method will describe a circular path.
  * @param rect:   Frame of Rectangular Path
  * @param cornerRadius:   Rectangular rounded radius
  */
+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect 
                              cornerRadius:(CGFloat)cornerRadius;

Which corner of a rectangle can be specified as a rounded corner:

/**
  * This method will create a closed path, which will draw straight lines and curves clockwise.  
  * @param rect:   Frame of Rectangular Path
  * @param corners:   UIRectCorner Enumeration type, specifying which corner of a rectangle becomes a rounded corner
  * @param cornerRadii:   Rectangular rounded radius
  */
+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect 
                         byRoundingCorners:(UIRectCorner)corners
                               cornerRadii:(CGSize)cornerRadii;
  • by arc
/**
  * This method creates an open path, and the arc is part of the circle. In the default coordinate system, the starting and ending angles are based on the unit circle (see the figure below). After calling this method, the current Point will be set to the end of the circle.
  * For example, by specifying the actual angle to be 0, the end angle to be pi, and the clockwise attribute to be YES, the lower half of the circle will be drawn.
  * However, when we do not modify the starting and ending angles, we only set the clockwise angle to NO, then we will draw the upper half of a circle.
  * @param center:   Center of a circle
  * @param radius: radius
  * @param startAngle:   Initial angle
  * @param endAngle:   End Angle
  * @param clockwise:   Whether to draw clockwise
  */
+ (instancetype) bezierPathWithArcCenter:(CGPoint)center 
                                  radius:(CGFloat)radius 
                              startAngle:(CGFloat)startAngle 
                                endAngle:(CGFloat)endAngle 
                               clockwise:(BOOL)clockwise;

)

  • by CGPath
+ (instancetype) bezierPathWithCGPath:(CGPathRef)CGPath;
  • by is the reverse direction Path (where the direction refers to the drawing direction).
/**
  * @return: Returns a new UIBezierPath object with the same shape as the original path.
  *          But the direction of drawing is opposite.
  */
- (UIBezierPath *) bezierPathByReversingPath;

Construct or add paths:

  • Move the current Point of the object to a point. (For most Path-Related methods, you need to call this method before you draw a line or curve.)
/**
  * If there are currently subpaths being drawn, the method implicitly terminates the current path. 
  * The current Point is set to the specified point. When the previous subpath is terminated, the method
  * It's not really going to close the last subpath. So the last one starts at the beginning of the path.
  * The endpoint is not linked.
  * @param point:   A point in the current coordinate system
  */
- (void)moveToPoint:(CGPoint)point;
  • Add a straight line
/**
  * This method will link a straight line from the current Point to the specified point. 
  * Note: After appending the line, the method updates current Point as the specified point.
  *       Before calling this method, you must first set the current Point. If the path is currently drawn
  *       If it is empty and currentPoint is not set, then calling this method will not produce anything.
  *       Effect.
  * @param point:   Drawing the end coordinate of a line, a point in the current coordinate system
  */
- (void)addLineToPoint:(CGPoint)point;
  • Add an arc
/**
  * This method will add a specified arc from the current Point.
  * The introduction of this method is the same as that of the construction method. Please go to see above.
  * @param center: Center of a circle
  * @param radius: radius
  * @param startAngle: Initial angle
  * @param endAngle: End Angle
  * @param clockwise: Whether to draw clockwise
  */
- (void)addArcWithCenter:(CGPoint)center 
                  radius:(CGFloat)radius 
              startAngle:(CGFloat)startAngle 
                endAngle:(CGFloat)endAngle 
               clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
  • Adding a cubic Bessel curve
/**
  * This method will add a cubic Bessel curve from the current Point to the specified endPoint.
  * The bending of a cubic Bessel curve is controlled by two control points. As shown in the figure below.
  * Note: Before calling this method, you must first set the current Point if the path is empty. 
  *       And currentPoint hasn't been set yet, calling this method will not produce any effect. 
  *       When the Bessel curve is added, the method will automatically update the current Point to
  *       Designated endpoint
  * @param endPoint: End
  * @param controlPoint1: Control point 1
  * @param controlPoint2: Control Point 2
  */
- (void)addCurveToPoint:(CGPoint)endPoint 
          controlPoint1:(CGPoint)controlPoint1 
          controlPoint2:(CGPoint)controlPoint2;
Illustration:

)

  • Adding a quadratic Bessel curve:
/**
  * This method will add a quadratic Bessel curve from the current Point to the specified endPoint.
  * currentPoint,endPoint,controlPoint The relationship between them finally defines the shape of the quadratic Bessel curve.
  * The bending of a quadratic Bessel curve is controlled by a control point. As shown in the figure below.
  * Note: Before calling this method, you must first set the current Point if the path is empty. 
  *       And currentPoint hasn't been set yet, calling this method will not produce any effect. 
  *       When the Bessel curve is added, the method will automatically update the current Point to
  *       Designated endpoint
  * @param endPoint: End
  * @param controlPoint: control point
  */
- (void)addQuadCurveToPoint:(CGPoint)endPoint 
               controlPoint:(CGPoint)controlPoint;
Illustration:

  • Adding UIBezierPath instance objects
/**
  * This method will be appended to the path of the current UIBezierPath object
  * Content in the specified UIBezierPath object. 
  */
- (void)appendPath:(UIBezierPath *)bezierPath;

Virtual path:

  • Construct a virtual path:
/**
  * @param pattern: This property is an array of C languages, where each element is CGFloat
  *                 The elements in the array represent the length of each part of the line segment, and the first element represents the first line of the line segment.
  *                 The second element represents the first clearance in the line segment. The values in this array take turns. Explain.
  *                 What is rotation? 
  *                 For example, declare an array CGFloat dash []= @{3.0, 1.0}; 
  *                 This means that the first part of the dotted line is 3.0 in length, the first gap is 1.0 in length, and the dotted line is 1.0 in length.
  *                 The length of the second part is 3.0, the length of the second gap is 1.0, and so on.

  * @param count: This parameter is the number of pattern arrays
  * @param phase: This parameter represents where dotted lines begin to be drawn.
  *                 For example: this is phase 6. pattern []= @{5, 2, 3, 2}; then the dotted line will be
  *                 The middle part of the first clearance begins to be drawn. If it's not very clear, please continue to look down.
  *                 The dotted line will be explained in the actual combat section below.
  */
- (void)setLineDash:(const CGFloat *)pattern
              count:(NSInteger)count
              phase:(CGFloat)phase;
  • Get the dotted line mode:
/**
  * This method can retrieve the dotted line style set before.
  *  Note:  pattern The capacity of this parameter must be greater than that of the method returning the array.
  *         If the capacity of the array cannot be determined, the method can be called twice, the first time
  *         When this method is called, the count parameter is passed in, and then the count parameter is used.
  *         To apply for the memory space of the pattern array. Then call this method normally the second time.
  */
- (void)getLineDash:(CGFloat *)pattern 
              count:(NSInteger *)count
              phase:(CGFloat *)phase;
Demo:
- (void) typeDashLine {

    // 1. Create three paths first. Comparisons are more helpful to understand.
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint: CGPointMake(80, 40)];
    [path addLineToPoint: CGPointMake(self.frame.size.width - 40, 40)];
    path.lineWidth = 2;


    UIBezierPath *path1 = [UIBezierPath bezierPath];
    [path1 moveToPoint: CGPointMake(80, 80)];
    [path1 addLineToPoint: CGPointMake(self.frame.size.width - 40, 80)];
    path1.lineWidth = 2;


    UIBezierPath *path2 = [UIBezierPath bezierPath];
    [path2 moveToPoint: CGPointMake(80, 120)];
    [path2 addLineToPoint: CGPointMake(self.frame.size.width - 40, 120)];
    path2.lineWidth = 2;

    // 2. This part is the specification of three paths dotted line, the main point is this part.
    CGFloat dashLineConfig[] = {8.0, 4.0};
    [path setLineDash: dashLineConfig
                           count: 2
                          phase: 0];


    CGFloat dashLineConfig1[] = {8.0, 4.0, 16.0, 8.0};
    [path1 setLineDash: dashLineConfig1
                count: 4
                phase: 0];


    CGFloat dashLineConfig2[] = {8.0, 4.0, 16.0, 8.0};
    [path2 setLineDash: dashLineConfig2
                count: 4
                phase: 12];

    // 3. Drawing
    [[UIColor orangeColor] set];
    [path stroke];
    [path1 stroke];
    [path2 stroke];
}

Display effect:


)

Change the path:

  • Close the current subpath:
/**
  * This method will draw a straight line from the current Point to the starting point of the subpath. 
  * This closes the current self-path. Next, the method updates the current Point.
  * For the end of the line just added, that is, the starting point of the current subway.
  */
- (void)closePath;
  • Deleting all points in the UIBezierPath object is equivalent to deleting all subpaths:
- (void)removeAllPoints;
  • Cut Path:
/**
  *  This method will modify the visual area of the current drawing context.
  *  When this method is called, it results in all subsequent rendering
  *  Operations will only take place in the cut-off area, out of the area.
  *  Content will not be rendered.
  *  If you want to execute the next drawing, delete the clipping area.
  *  Then you have to use the CGContextSaveGState method before calling this method.
  *  Save the current drawing status when you no longer need this clipping area
  *  When you use CGContextRestoreGState, you only need to use the CGContextRestoreGState method.
  *  To restore the drawing state you saved before.
  * @param blendMode: Mixed patterns determine how and
  *                   Composition of existing rendered content
  * @param alpha: Transparency when filling paths
  */
- (void)addClip;
  • Radiation conversion operation:
/**
  * This method will radiate all points in the path directly.
  * Transform operation. 
  */
- (void)applyTransform:(CGAffineTransform)transform;

Drawing correlation:

  • Fill path:
/**
  * The current filling color and drawing attributes of the method fill the closed area of the path.
  * If the current path is an open path, the method implicitly closes the path and fills it up.
  * This method automatically saves the state of the current drawing before filling, so we don't need it.
  * Save the drawing status manually. 
  */
- (void)fill;
  • Fill paths with mixed mode:
/**
  * The current fill color and drawing attributes of the method (plus the specified blending mode and transparency) 
  * Fill in the closed area of the path. If the current path is an open path, the method will
  * It implicitly closes the path and fills it
  * This method automatically saves the state of the current drawing before filling, so we don't need it.
  * Save the drawing status manually. 
  *
  * @param blendMode: Mixed patterns determine how to synthesize existing rendered content
  * @param alpha: Transparency when filling paths
  */
- (void)fillWithBlendMode:(CGBlendMode)blendMode 
                    alpha:(CGFloat)alpha;
  • Drawing the path: (generally used for the last step after the Path-Related settings have been completed).
- (void)stroke;

Judgment method:

  • Does it contain a point?
/**
  *  This method returns a Boolean value when the coverage area of the curve contains
  * The specified point (interior point) returns YES or NO. 
  * Note: If the current path is an open one, then
  *       Even if the specified point is within the path coverage, the method will still do so.
  *       Return NO, so if you want to determine whether a point is in one or not
  *       When you are within the scope of an open path, you need to Copy a path first.
  *       And call - (void)closePath; close the path, and then
  *       Call this method again to determine if the specified point is an internal point.
  * @param point: Specify points.
  */
- (BOOL) containsPoint:(CGPoint)point;

Demo example:

  • Attribute method is simple to apply:
    //========================================================
    //Part one: Create UIBezierPath objects.
    //========================================================

    //1. Through the rectangle:
    UIBezierPath * rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 30, 50)];
    /**
     Judgment correlation:
     */
    //ONE: Is it included?
    BOOL isContain = [rectPath containsPoint:CGPointMake(25, 24)];
    if (isContain) {
        NSLog(@"contain");
    }
    //TWO: Is the path empty?
    BOOL isEmpty = [rectPath isEmpty];
    if (!isEmpty) {
        NSLog(@"not empty");
    }

    /**
     Relevant attributes:
     */
    rectPath.lineWidth = 3;
    rectPath.lineCapStyle = kCGLineCapSquare; //The end-point pattern of the curve is not suitable for closed paths.
    rectPath.lineJoinStyle = kCGLineJoinMiter;  //Curve connection style.
    rectPath.miterLimit = 2; //Maximum distance between inside and outside angles.
    rectPath.flatness = 0.6; //Default 0.6, the lower the accuracy, the higher.
    rectPath.usesEvenOddFillRule = YES; //Whether the parity filling rule is applicable or not. Default non-zero rule filling.
    NSLog(@"Covered rectangular area:%@",[NSValue valueWithCGRect:rectPath.bounds]);//Judging the Covered Rectangular Area

    [[UIColor orangeColor]set];
    [rectPath stroke];

    //2. By ellipse (tangent ellipse of rectangle)
    UIBezierPath * ovalPath= [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 70, 30, 50)];
    [[UIColor redColor]set];
    [ovalPath stroke];

    //3. Through a rounded rectangle:
    UIBezierPath * cornerRectPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 130, 30, 50) cornerRadius:3];
    [[UIColor blackColor] set];
    [cornerRectPath stroke];

    //4. Through the arc:
    UIBezierPath * arcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(60, 200) radius:50 startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
    [[UIColor brownColor] set];
    arcPath.lineWidth = 4;
    [arcPath stroke];

    //5. Pass the path.
    CGPathRef  pathRef = CGPathCreateWithRect(CGRectMake(0, 280, 30, 50), nil);
    UIBezierPath * pathPath = [UIBezierPath bezierPathWithCGPath:pathRef];
    [[UIColor redColor]set];
    [pathPath stroke];


    //========================================================
    //Part 2: Construction Path:
    //========================================================
    UIBezierPath * allocPath = [UIBezierPath bezierPath];
    allocPath.lineWidth= 4;
    [allocPath moveToPoint:CGPointMake(50, 350)];

    //Add a straight line path:
//    [allocPath addLineToPoint:CGPointMake(50, 400)];

    //Add an arc: (when the distance from the starting point to the center of the circle is greater than the radius, the starting point to the point from the radius of the center of the circle will make a straight line)
//    [allocPath addArcWithCenter:CGPointMake(50, 400) radius:50 startAngle:M_PI_2+M_PI endAngle:M_PI_2 clockwise:NO];

    //Adding a cubic Bessel curve:
//    [allocPath addCurveToPoint:CGPointMake(50, 400) controlPoint1:CGPointMake(60, 370) controlPoint2:CGPointMake(30, 385)];

    //A quadratic Bessel curve is added.
    [allocPath addQuadCurveToPoint:CGPointMake(50, 400) controlPoint:CGPointMake(30, 385)];
    //Close the path:
    [allocPath closePath];
    //Delete all points:
//    [allocPath removeAllPoints];
    //Add a path to the current path.
//    [allocPath appendPath:pathPath];
    //Fill;
//    [allocPath fill];
    //Choose the mixing method to fill in. / /?
//    [allocPath fillWithBlendMode:kCGBlendModeLighten alpha:0.8];



    [[UIColor orangeColor] set];
    [allocPath stroke];


    [[UIColor redColor]setFill];

    UIRectFill(CGRectMake(100, 50, 100, 50));

CAShapeLayer

Attributes:

Most of the attributes are similar to those of UIBezierPath.

  • Path: CGPathRef object, graphical side path.

  • FilColor: CGColorRef object, graphics fill color, default to black.

  • Fill Rule: Fill rules. The fillMode attribute similar to UIBezierPath.

KCAFill Rule NonZero: Non-zero surround rule.

This rule determines the "internal region" by drawing rays from a point on canvas in any direction to infinity, and then checking the intersection of lines and rays. Counting begins at 0, and every time the path line passes through the ray from left to right, it adds one and decreases one from right to left. By calculating the intersection point, if the result is 0, then the point is outside the path, otherwise it is inside.


KCAFill Rule EvenOdd: Parity Principle.

By drawing rays from a point on canvas in any direction to infinity, the number of line path and intersection points on a given graph is calculated. If the number is odd, then the point is inside the graph; if it is even, the point is outside the graph.


svg
  • strokeColor: Edge color.

  • LineDashPhase: The starting position of the sideline style, that is, if the lineDashPattern s are set to @ [2, 2, 3, 4], the lineDashPhase is the starting position of the first line with a length of 2.

  • StrkeStart, StrkeEnd: [0, 1] denotes the beginning and end of the line.
  • LineDashPattern: An array of NSNumbers representing the length of a single line and the length of a blank line in turn.

DEMO:

    //   Create a path object
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    //  Starting point
    [linePath moveToPoint:(CGPoint){20,20}];
    // Other points
    [linePath addLineToPoint:(CGPoint){180,160}];
    [linePath addLineToPoint:(CGPoint){200,50}];
    [linePath addLineToPoint:CGPointMake(250, 250)];

    //  Setting Path Canvas
    CAShapeLayer *lineLayer = [CAShapeLayer layer];
    lineLayer.bounds = (CGRect){0,0,200,200};
    lineLayer.position = CGPointMake(100, 100);
    lineLayer.lineWidth = 2.0;
    lineLayer.strokeColor = [UIColor blueColor].CGColor; //   border color

    lineLayer.path = linePath.CGPath;
    lineLayer.fillColor  = nil;   //  The default is black

    //  Add to Layer
    [self.layer addSublayer:lineLayer];

More Demo visible Study on Bessel Curve and CAShape Layer

Summary:

UIBezierPath draws graphics in the current drawing context. Because creating, configuring, rendering paths and other operations are completely different steps, you can easily reuse UIBezierPath objects in your code. You can even use the same UIBezierPath object to render the same graphics. Many times, you can also modify attributes to render different styles of paths at multiple rendering intervals.

When you have configured the geometric path and drawing attributes for the UIBezierPath object, you can use the stroke and fill methods to draw in the current drawing context. The stroke method will use the current strokeColor and drawing attributes to draw the outline of the curve. Similarly, the fill method will use fillColor to fill in the curve. Graphics surrounded by paths (using UIColor class methods to set strokeColor and fillColor).

A large number of methodological understandings in this article originate from this article. UIBezier -- Brief Book Thank the author for his comprehensive and detailed analysis of UIBezier, and for providing a large number of interpretations of official documents, which has greatly benefited me.

We will continue to improve our understanding of UIBezierPath and some examples of its flexible use, which will be added to this article.

Topics: Attribute