iOS: UIBezierPath简介及静态图形实现

  • Post author:
  • Post category:其他


UIBezierPath介绍

基本介绍:

UIBezierPath可以用来创建矢量的路径和图形,使用此类可以定义各种图形。我们用直线和弧形的组合来创建复杂的图形。每一个直线段或者曲线段的结束的地方是下一个的开始的地方。每一个连接的直线或者曲线段的集合称为subpath。一个UIBezierPath对象的完整路径包括一个或者多个subpath。


UIBezierPath有两种实现方法:

1.使用CAShapeLayer与UIBezierPath配合画出一些想要的图形 。示例代码如下:

UIBezierPath * path = [[UIBezierPath alloc]init];
[path moveToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(200, 100)];
CAShapeLayer * testLayer = [CAShapeLayer layer];
testLayer.path = path.CGPath;
testLayer.lineWidth = 10;
testLayer.fillColor = [UIColor redColor].CGColor;
testLayer.strokeColor = [UIColor blackColor].CGColor;
testLayer.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer];

2.在UIView的子类中重写drawRect方法,视图的 drawRect方法在执行之前,系统首先为视图的图层创建一个图形上下文,然后为绘画状态设置一些默认参数。所有就不需要CAShapeLayer的配合了。示例代码如下:

- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(100 , 10)];
[path addLineToPoint:CGPointMake(200, 10)];
path.lineWidth = 15;
path.lineCapStyle = kCGLineCapSquare;
[[UIColor yellowColor] setStroke];
[path stroke];
}

上面两段代码的效果图如下所示

图中黑色圆形端点的线条是用CAShapeLayer与UIBezierPath配合画的。黄色方形端点的线条是重写了drawRect方法画出来的。

下面我们简单的介绍一下UIBezierPath的属性

1.CGPath:将UIBezierPath类转换成CGPath。

2.empty:检测当前路径是否绘制过直线或曲线,就算你仅仅调用了 moveToPoint方法,那么当前路径也被看做不为空。

3.bounds:该属性描述的是一个能够完全包含路径中所有点的一个最小的矩形区域. 该区域包含二次贝塞尔曲线和三次贝塞尔曲线的控制点。

4.currentPoint:当前path的位置,可以理解为path的终点。

5.lineWidth:path宽度

6.lineCapStyle:path端点样式。

kCGLineCapButt:无端点

kCGLineCapRound:圆形端点

kCGLineCapSquare:方形端点(样式上和kCGLineCapButt是一样的,但是比kCGLineCapButt稍长)

7.lineJoinStyle:拐角样式

kCGLineJoinMiter:尖角

kCGLineJoinRound:圆角

kCGLineJoinBevel:斜面角

8.miterLimit:最大斜接长度(只有在使用kCGLineJoinMiter时才有效), 边角的角度越小,斜接长度就会越大为了避免斜接长度过长,使用lineLimit属性限制,如果斜接长度超过miterLimit,边角就会以KCALineJoinBevel类型来显示。

9.flatness:弯曲路径的渲染精度,默认为0.6。值越小精度越高,渲染精度也就越高, 会产生相对更平滑的曲线,但是需要花费更多的计算时间。

10.usesEvenOddFillRule:设置为 YES, 则路径将会使用基偶规则 (even-odd) 进行填充,设置为 NO, 则路径将会使用 非零规则 (non-zero) 规则进行填充,默认是NO。

对这个属性的具体解释可以看下面这个链接:

https://developer.apple.com/reference/uikit/uibezierpath/1624360-usesevenoddfillrule?language=objc

11.UIRectCorner: 指定矩形的哪个角变为圆角

UIRectCornerTopLeft:左上角

UIRectCornerTopRight:右上角

UIRectCornerBottomLeft:左下角

UIRectCornerBottomRight:右下角

UIRectCornerAllCorners:所有四个角


UIBezierPath 相关API介绍

    • (instancetype)bezierPath:

      创建一个UIBezierPath对象
    • (instancetype)bezierPathWithRect:(CGRect)rect:

      该方法将会创建一个闭合路径, 起始点是 rect 参数的的 origin, 并且按照顺时针方向添加直线, 最终形成矩形
    • (instancetype)bezierPathWithOvalInRect:(CGRect)rect:

      参数:

      rect->矩形的Frame

      根据传入的rect矩形参数绘制一个内切曲线。当传入的rect是一个正方形时,绘制的图像是一个内切圆;当传入的rect是一个长方形时,绘制的图像是一个内切椭圆
    • (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius

      参数:

      rect->矩形的Frame

      cornerRadius->圆角大小

      根据一个Rect 画一个圆角矩形曲线 当Rect为正方形时且Radius等于边长一半时 画的是一个圆
    • (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii

      参数:

      rect->矩形的Frame

      corners->指定的圆角

      cornerRadii->圆角的大小

      该方法作用同上,不同的地方就是你可以指定某一个角或者几个角为圆角
    • (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise

      参数:

      center->圆点

      radius->半径

      startAngle->起始位置

      endAngle->结束为止

      clockwise->是否顺时针方向

      指定中心点跟半径画弧线或者圆
    • (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath

      参数:

      CGPath->已有路径

      根据CGPath创建并返回一个新的UIBezierPath对象
    • (instancetype)init
    • (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
    • (CGPathRef)CGPath

      转换成CGPath:
    • (void)moveToPoint:(CGPoint)point

      参数:

      point->目标位置

      设置UIBezierPath的起始点
    • (void)addLineToPoint:(CGPoint)point

      参数:

      point->目标位置

      绘制一条线:
    • (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2

      参数:

      endPoint->终点

      controlPoint1->控制点1

      controlPoint2->控制点2

      创建三次贝塞尔曲线:以三个点画一段曲线,一般和moveToPoint:配合使用。其实端点为moveToPoint:设置,终止端点位为endPoint;控制点1的坐标controlPoint1,这个参数可以调整。控制点2的坐标是controlPoint2
    • (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint

      参数:

      endPoint->终点

      controlPoint->控制点

      创建二次贝塞尔曲线:通过调用此方法来实现的。一般和moveToPoint:配合使用。endPoint终端点,controlPoint控制点,对于二次贝塞尔曲线,只有一个控制点
    • (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise

      参数:参看创建圆弧

      添加一个弧线, bezierPathWithArcCenter是初始化一个弧线,addArcWithCenter是添加一个弧线,共同点就是参数都一样
    • (void)closePath;

      闭合路径,即在终点和起点连一根线:
    • (void)removeAllPoints;

      清空路径:
    • (void)appendPath:(UIBezierPath *)bezierPath

      参数:

      bezierPath->追加的路径

      追加路径:
    • (UIBezierPath *)bezierPathByReversingPath

      扭转路径,即起点变成终点,终点变成起点:
    • (void)applyTransform:(CGAffineTransform)transform;

      参数:

      transform->仿射变换

      路径进行仿射变换,用指定的仿射变换矩阵变换路径的所有点
    • (void)setLineDash:(nullable const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase

      参数:

      pattern->C类型线性数据

      count->pattern中数据个数

      phase-> 起始位置

      绘制虚线
    • (void)fill

      填充颜色
    • (void)stroke;

      绘制,路径创建需要绘制才能显示出来
    • [[UIColor blackColor] setStroke];

      设置描边颜色,需要在设置后调用描边方法:
    • [[UIColor redColor] setFill];

      设置填充颜色,需要在设置后调用填充方法
    • (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha

      参数:

      blendMode->混合模式

      alpha->透明度

      用指定的混合模式和透明度值来描绘受接收路径所包围的区域(These methods do not affect the blend mode or alpha of the current graphics context.)
    • (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;

      参数:

      blendMode->混合模式

      alpha->透明度

      使用指定的混合模式和透明度值沿着接收器路径。绘制一行
    • (void)addClip;

      修改当前图形上下文的绘图区域可见,随后的绘图操作导致呈现内容只有发生在指定路径的填充区域

具体实例

直线图形的效果图以及代码已经在开篇就已经说过了,下面我们看一些其他图形的例子。

实例 1:

弧线、弧形和扇形的实现。

效果如下图所示:

代码如下:

-(void)drawArc{
  CGFloat radius = 50.0;
  CGFloat centerx = self.view.frame.size.width/2-25;
  //code 1
  UIBezierPath *path = [UIBezierPath bezierPath];
  [path addArcWithCenter:CGPointMake(centerx, 50+64) radius:radius startAngle:0.0 endAngle:M_PI_2 clockwise:YES];
  [path stroke];
 CAShapeLayer * testLayer = [CAShapeLayer layer];
  testLayer.path = path.CGPath;
  testLayer.lineWidth = 5;
  testLayer.fillColor = [UIColor clearColor].CGColor;
  testLayer.strokeColor = [UIColor blackColor].CGColor;
  testLayer.lineCap = kCALineCapRound;
  [self.view.layer addSublayer:testLayer];
  //code 2
  UIBezierPath *path1 = [UIBezierPath bezierPath];
  [path1 moveToPoint:CGPointMake(centerx, 50+64+70)];
  [path1 addArcWithCenter:CGPointMake(centerx, 50+64+70) radius:radius startAngle:0 endAngle:M_PI_2 clockwise:YES];
  CAShapeLayer * testLayer1 = [CAShapeLayer layer];
  [testLayer1 setPath:path1.CGPath];
  [testLayer1 setFillColor:[UIColor yellowColor].CGColor];
  [testLayer1 setStrokeColor:[UIColor purpleColor].CGColor];
  [self.view.layer addSublayer:testLayer1];
  //code 3
  UIBezierPath *path2 = [UIBezierPath bezierPath];
  [path2 addArcWithCenter:CGPointMake(centerx, 50+64+140) radius:radius startAngle:0 endAngle:M_PI_2 clockwise:YES];
  [path2 closePath];
  CAShapeLayer * testLayer2 = [CAShapeLayer layer];
  [testLayer2 setPath:path2.CGPath];
  [testLayer2 setFillColor:[UIColor whiteColor].CGColor];
  [testLayer2 setStrokeColor:[UIColor blueColor].CGColor];
  [self.view.layer addSublayer:testLayer2];
}

code 1、code 2、code3分别对应上面的弧线,扇形,弧形。

实例 2:

圆形已经扇形图的实现。

具体效果请看下图:

具体代码如下所示:

-(void)drawCircle
{
CGFloat radius = 50.0;
CGFloat centerx = self.view.frame.size.width/2-25;

//code 1
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:CGPointMake(centerx, 50+64+5) radius:radius startAngle:0.0 endAngle:2*M_PI clockwise:YES];
[path stroke];
CAShapeLayer * testLayer = [CAShapeLayer layer];
testLayer.path = path.CGPath;
testLayer.lineWidth = 5;
testLayer.fillColor = [UIColor clearColor].CGColor;
testLayer.strokeColor = [UIColor blackColor].CGColor;
testLayer.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer];

//code 2
UIBezierPath *path1 = [UIBezierPath bezierPath];
[path1 addArcWithCenter:CGPointMake(centerx, 50+64+5+110) radius:radius startAngle:0.0 endAngle:2*M_PI clockwise:YES];
[path1 stroke];
CAShapeLayer * testLayer1 = [CAShapeLayer layer];
testLayer1.path = path1.CGPath;
testLayer1.lineWidth = 2;
testLayer1.fillColor = [UIColor redColor].CGColor;
testLayer1.strokeColor = [UIColor yellowColor].CGColor;
testLayer1.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer1];

//code 3
UIBezierPath*  path2 = [[UIBezierPath alloc] init];
[path2 moveToPoint:CGPointMake(centerx, 50+64+5+220)];
[path2 addArcWithCenter:CGPointMake(centerx, 50+64+5+220) radius:radius startAngle:0 endAngle:M_PI_2 clockwise:YES];
[path2 addLineToPoint:CGPointMake(centerx, 50+64+5+220)];
[path2 closePath];
CAShapeLayer * testLayer2 = [CAShapeLayer layer];
testLayer2.path = path2.CGPath;
testLayer2.lineWidth = 2;
testLayer2.fillColor = [UIColor redColor].CGColor;
testLayer2.strokeColor = [UIColor whiteColor].CGColor;
testLayer2.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer2];

UIBezierPath*  path3 = [[UIBezierPath alloc] init];
[path3 moveToPoint:CGPointMake(centerx, 50+64+5+220)];
[path3 addArcWithCenter:CGPointMake(centerx, 50+64+5+220) radius:radius startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
[path3 addLineToPoint:CGPointMake(centerx, 50+64+5+220)];
[path3 closePath];
CAShapeLayer * testLayer3 = [CAShapeLayer layer];
testLayer3.path = path3.CGPath;
testLayer3.lineWidth = 2;
testLayer3.fillColor = [UIColor yellowColor].CGColor;
testLayer3.strokeColor = [UIColor whiteColor].CGColor;
testLayer3.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer3];

UIBezierPath*  path4 = [[UIBezierPath alloc] init];
[path4 moveToPoint:CGPointMake(centerx, 50+64+5+220)];
[path4 addArcWithCenter:CGPointMake(centerx, 50+64+5+220) radius:radius startAngle:M_PI endAngle:2*M_PI clockwise:YES];
[path4 addLineToPoint:CGPointMake(centerx, 50+64+5+220)];
[path4 closePath];
CAShapeLayer * testLayer4 = [CAShapeLayer layer];
testLayer4.path = path4.CGPath;
testLayer4.lineWidth = 2;
testLayer4.fillColor = [UIColor blueColor].CGColor;
testLayer4.strokeColor = [UIColor whiteColor].CGColor;
testLayer4.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer4];
}

code 1、 code 2、code3分别对应上图的空心圆,实心圆以及扇形图。

实例 3:

椭圆的实现。

具体的效果请看下图:

代码如下:

-(void)drawEllipse
{
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:(CGRect){80,50+64,150,100}];
CAShapeLayer * testLayer = [CAShapeLayer layer];
testLayer.path = path.CGPath;
testLayer.lineWidth = 2;
testLayer.fillColor = [UIColor redColor].CGColor;
testLayer.strokeColor = [UIColor blackColor].CGColor;
[self.view.layer addSublayer:testLayer];
}

实例 4:

不规则形状的实现。

这里我只实现了一个不规则五边形和两个圆角的矩形。具体效果请看图:

代码:

-(void)otherGraph
{
//code 1
UIBezierPath * path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(100, 80)];
[path addLineToPoint:CGPointMake(200, 120)];
[path addLineToPoint:CGPointMake(160, 140)];
[path addLineToPoint:CGPointMake(40.0, 140)];
[path addLineToPoint:CGPointMake(10.0, 120)];
[path closePath];
CAShapeLayer * testLayer = [CAShapeLayer layer];
testLayer.path = path.CGPath;
testLayer.lineWidth = 2;
testLayer.fillColor = [UIColor redColor].CGColor;
testLayer.strokeColor = [UIColor blackColor].CGColor;
[self.view.layer addSublayer:testLayer];

//code 2
UIBezierPath *path1 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 200, 150, 100) byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomRight cornerRadii:CGSizeMake(10, 10)];
CAShapeLayer * testLayer1 = [CAShapeLayer layer];
testLayer1.path = path1.CGPath;
testLayer1.lineWidth = 2;
testLayer1.fillColor = [UIColor clearColor].CGColor;
testLayer1.strokeColor = [UIColor blackColor].CGColor;
testLayer1.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer1];

}

code 1对应不规则五边形,code 2对应带圆角的矩形。

实例 5:

渐变色的实现。效果请看图:

代码如下:

-(void)drawColor
{
//code 1
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:50 startAngle:0.0 endAngle:M_PI_2 clockwise:NO];
CAShapeLayer * testLayer = [CAShapeLayer layer];
testLayer.path = path.CGPath;
testLayer.fillColor = [UIColor clearColor].CGColor;
testLayer.strokeColor = [UIColor blackColor].CGColor;
testLayer.lineWidth = 15;

CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.view.frame;
gradientLayer.colors = @[(__bridge id)[UIColor greenColor].CGColor,(__bridge id)[UIColor blueColor].CGColor ];
gradientLayer.startPoint = CGPointMake(0,0.5);
gradientLayer.endPoint = CGPointMake(1,0.5);

[self.view.layer addSublayer:gradientLayer];
gradientLayer.mask = testLayer;

//code 2
UIBezierPath * path1 = [UIBezierPath bezierPath];
[path1 moveToPoint:CGPointMake(100, 280)];
[path1 addLineToPoint:CGPointMake(200, 300)];
[path1 addLineToPoint:CGPointMake(250, 350)];
[path1 addLineToPoint:CGPointMake(150, 400)];
[path1 addLineToPoint:CGPointMake(50, 300)];
[path1 closePath];

CAShapeLayer * testLayer1 = [CAShapeLayer layer];
testLayer1.path = path1.CGPath;
testLayer1.lineWidth = 2;
testLayer1.fillColor = [UIColor redColor].CGColor;
testLayer1.strokeColor = [UIColor blackColor].CGColor;

CAGradientLayer *gradientLayer1 = [CAGradientLayer layer];
gradientLayer1.frame = self.view.frame;
gradientLayer1.colors = @[(__bridge id)[UIColor yellowColor].CGColor, (__bridge id)[UIColor purpleColor].CGColor ];
gradientLayer1.startPoint = CGPointMake(0,0.5);
gradientLayer1.endPoint = CGPointMake(1,0.5);

[self.view.layer addSublayer:gradientLayer1];
gradientLayer1.mask = testLayer1;
}

code 1对应渐变色弧形的实现,code 2对应渐变色多边形的实现。渐变色的实现依赖于CAGradientLayer。想详细了解的同学请自己搜索。

实例 6:

不规则图片的实现。

代码如下:

-(void)drawImage
{
UIImage * image = [UIImage imageNamed:@"gggggg"];
UIBezierPath *path = [[UIBezierPath alloc] init];
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[path moveToPoint:CGPointMake(self.view.frame.size.width/2, 100)];
[path addArcWithCenter:CGPointMake(self.view.frame.size.width/2, 200) radius:150 startAngle:0 endAngle:M_PI clockwise:YES];
[path closePath];

CAShapeLayer * testLayer = [[CAShapeLayer alloc] init];
testLayer.frame = self.view.bounds;
testLayer.fillColor = [UIColor blackColor].CGColor;
testLayer.strokeColor = [UIColor redColor].CGColor;
testLayer.contentsScale = [UIScreen mainScreen].scale;
testLayer.path = path.CGPath;

CALayer * contentLayer = [CALayer layer];
contentLayer.frame = self.view.bounds;
contentLayer.contentsGravity = kCAGravityResizeAspectFill;
contentLayer.mask = testLayer;
contentLayer.contents = (__bridge id _Nullable)(image.CGImage);
[self.view.layer addSublayer:contentLayer];
}

实例 7:

二次贝塞尔曲线和三次贝塞尔曲线的实现:

代码如下:

-(void)drawBezierCurve
{
//code 1
UIBezierPath* path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 150)];
[path addQuadCurveToPoint:CGPointMake(150, 160) controlPoint:CGPointMake(270, 100)];
CAShapeLayer * testLayer = [CAShapeLayer layer];
testLayer.path = path.CGPath;
testLayer.lineWidth = 2;
testLayer.fillColor = [UIColor redColor].CGColor;
testLayer.strokeColor = [UIColor blackColor].CGColor;
[self.view.layer addSublayer:testLayer];

//code 2
UIBezierPath* path1 = [UIBezierPath bezierPath];
[path1 moveToPoint:CGPointMake(20, 260)];
[path1 addCurveToPoint:CGPointMake(290, 280) controlPoint1:CGPointMake(50, 20) controlPoint2:CGPointMake(120, 380)];
[path1 stroke];
CAShapeLayer * testLayer1 = [CAShapeLayer layer];
testLayer1.path = path1.CGPath;
testLayer1.lineWidth = 2;
testLayer1.fillColor = [UIColor redColor].CGColor;
testLayer1.strokeColor = [UIColor blackColor].CGColor;
testLayer1.lineCap = kCALineCapRound;
[self.view.layer addSublayer:testLayer1];
}

code 1对应二次贝塞尔曲线的实现,code 2对应三次贝塞尔曲线的实现。

GitHub上,地址如下:


https://github.com/BigDad3/BezierPath

转载:


https://www.jianshu.com/p/81d83fd4a059