Quartz 2D绘制路径、文字

最近都是对UI部分的研究,特别是Quartz2D部分。将会有一波相关文章来袭~~~今天记录的是绘制路径和绘制文字。

一. Quartz 2D简介:

什么是Quartz 2D?

Quartz 2D是一个二维绘图引擎,支持iOS和mac系统。

Quartz 2D可以做的事情:

  • 绘制图形: 线条、三角形、矩形、圆形、椭圆、弧形、扇形
  • 绘制文字
  • 绘制/生成图片(图像)
  • 读取/生成Pdf
  • 截图/剪裁图片
  • 自定义UI控件:虽然UIKit框架提供的控件可以实现UI界面,但是有些极其复杂、个性化的空间需要Quartz 2D才能画出来

例如:

  • 削圆


    Quartz 2D绘制路径、文字_第1张图片
    削圆
  • 绘图

  • 做手势解锁


    Quartz 2D绘制路径、文字_第2张图片
    手势解锁
  • 做折线图、饼状图、矩阵图(做复杂的我们可以使用Charts 这个第三方)

    Quartz 2D绘制路径、文字_第3张图片
    饼状图

二.图形上下文

在开始绘制之前,我们需要先知道图形上下文。

  • 图形上下文(Graphics Context):是一个CGContextRef类型的数据类型;
    我们可以把它理解为画板
  • 作用:
    1. 保存绘图信息、绘图状态
    2. 决定绘制的输出目标(绘制到什么地方,例如:PDF文件、Bitmap或者显示器的窗口上)

相同的一套绘图序列,指定不同的Graphic Context,就可以将相同的图像绘制到不同的目标

  • Quartz 2d提供的Graphics Context(图形上下文):
    Bitmap Graphics Context
    PDF Graphics Context
    Window Graphics Context
    Layer Graphics Context
    Printer Graphics Context
Quartz 2D绘制路径、文字_第4张图片
图形上下文的理解

三.路径绘制

绘图顺序

在view上面绘图需要做一下操作:

  1. 自定义view,在view的- (void)drawRect:(CGRect)rect方法里面获取图形上下文;

当view显示的时候调用 viewwillapper之后,didapper之前会调用此方法;
刷新视图,调用view的setNeedsDisplay方法的时候;

//作用:专门用来绘图的
//什么时候调用:当view显示的时候调用 viewwillapper之后,didapper之前
//参数:当前view的bounds
- (void)drawRect:(CGRect)rect{
}
  1. 获取当前跟view相关的图形上下文
  2. 描述路径
  3. 把路径添加到上下文
  4. 把上下文当中绘制的所有路径渲染到view的layer中

绘图前提

我们下面开始绘制路径,所有的操作都基于这部分:
在Main.storyboard的viewcontroller里面添加一个DrawView,所有的操作将在这个DrawView上面进行

Quartz 2D绘制路径、文字_第5张图片
绘图基础

总结:

直线、曲线调用实例方法绘制
矩形、圆形、椭圆调用类方法绘制
弧形、扇形调用实例、类方法都可以绘制

//直线
//生成路径
+ (instancetype)bezierPath;
//设置起点
- (void)moveToPoint:(CGPoint)point;
//设置直线终点
- (void)addLineToPoint:(CGPoint)point;

//曲线
//controlPoint是拐弯的点
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;  

//矩形
//rect:矩形的frame  x,y,宽、高
+ (instancetype)bezierPathWithRect:(CGRect)rect;

//圆形
//描述圆角矩形 cornerRadius:圆角半径
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius

//椭圆
rect :x,y,横直径、纵直径
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

//弧形、扇形
    //ArcCenter:所在圆的圆心
    //radius:半径
    //startAngle:开始角度;0度是在圆的最右侧;向上:度数是负的;向下:度数是正的
    //endAngle:截止角度(到哪个位置)
    //clockwise:是否为顺时针(怎么到达)
方法1:
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

方法2:
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);

1.绘制直线

  • 效果图
    我们要绘制的效果,添加两条直线:
Quartz 2D绘制路径、文字_第6张图片
直线效果图
  • 主要使用方法
//生成路径
+ (instancetype)bezierPath;
//设置起点
- (void)moveToPoint:(CGPoint)point;
//设置终点
- (void)addLineToPoint:(CGPoint)point;

1.1 绘制方法1

在drawRect方法内部会自动创建一个跟view相关联的上下文,所以我们在view的drawRect进行绘制;

- (void)drawRect:(CGRect)rect {
    //在drawRect方法内部会自动创建一个跟view相关联的上下文
    //可以直接获取
    //无论开启上下文,还是获取上下文,都是UIGraphics
 
    //1.获取当前跟view相关的图形上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    //2.描述路径:一个路径可以描述多条线
    UIBezierPath * path = [UIBezierPath bezierPath];
    //2.1设置起点
    //坐标原点是以当前绘制view的左上角为(0,0)
    [path moveToPoint:CGPointMake(10, 10)];
    //2.2添加一根线到某个点
    [path addLineToPoint:CGPointMake(100, 100)];
    
    //再添加一根线
    [path moveToPoint:CGPointMake(20, 10)];
    [path addLineToPoint:CGPointMake(200, 100)];

    //3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath);
    
    //4.把上下文当中绘制的所有路径渲染到view的layer中
    //渲染的方式有两种:
    //stroke:描边
    //fill:填充
    CGContextStrokePath(ctx);
  
}

注意渲染到view上面的方法有

    CGContextStrokePath(ctx);//描边写法
    CGContextFillPath(ctx);//填充写法

1.2 绘制方法2

上面我们要获取图形上下文,把路径添加到上下文,然后把绘制的路径渲染到view的layer中。我们还可以把上面的步骤简单化,按下面这种方式写:

- (void)drawRect:(CGRect)rect {

    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(10, 10)];
    [path addLineToPoint:CGPointMake(100, 100)];

    [path moveToPoint:CGPointMake(20, 10)];
    [path addLineToPoint:CGPointMake(200, 100)];

    //把绘制的图形添加到上下文、并且渲染到view的layer层上
    [path stroke];
}

注意同样的,这一种绘制方法里面,渲染的方法由两种,但是填充写法都是在图形上面才有填充效果,例如圆形、扇形,后面我们再演示:

    [path stroke];//描边写法
    [path fill];//填充写法

2. 绘制曲线

  • 效果图:
    我们要绘制的效果:


    Quartz 2D绘制路径、文字_第7张图片
    曲线效果图
  • 主要使用的这个方法:

//controlPoint是拐弯的点
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;  

绘制直线时候记录的两种方法,我们为了再记忆一下绘制顺序,继续使用第一种:

- (void)drawRect:(CGRect)rect {

    //1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    //2.描述路径
    UIBezierPath * path = [UIBezierPath bezierPath];
    //设置起点
    [path moveToPoint:CGPointMake(50, 100)];
    //添加一根曲线到某个点 controlPoint是拐弯的点
    [path addQuadCurveToPoint:CGPointMake(150, 100) controlPoint:CGPointMake(100, 50)];
    //3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath);
    //4.把上下问的内容渲染到view的layer
    CGContextStrokePath(ctx);
}
 

3. 绘制矩形

  • 效果图
    我们要绘制的效果图:


    Quartz 2D绘制路径、文字_第8张图片
    矩形效果图-1
  • 主要使用方法

//rect:矩形的frame  x,y,宽、高
+ (instancetype)bezierPathWithRect:(CGRect)rect;
- (void)drawRect:(CGRect)rect {
     //1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.描述路径
    UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 50)];
    //3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath);
    //4.把上下问的内容渲染到view的layer
    CGContextStrokePath(ctx);
}

上面我们使用的是描边显示,我们现在试一下填充方法(fill):

    //把上面的渲染方法换为填充
    CGContextFillPath(ctx);
Quartz 2D绘制路径、文字_第9张图片
矩形效果图- 2

4.圆

  • 效果图


    Quartz 2D绘制路径、文字_第10张图片
  • 主要使用方法
//描述圆角矩形 cornerRadius:圆角半径
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius

- (void)drawRect:(CGRect)rect {

    //绘制曲线
    UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 100, 100) cornerRadius:50];
    //[path fill];
    //绘制到view的layer上
    [path stroke];
}

5. 椭圆

  • 效果图


    Quartz 2D绘制路径、文字_第11张图片
    椭圆的效果图
  • 主要使用方法

rect :x,y,横直径、纵直径
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
- (void)drawRect:(CGRect)rect {
    UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 50, 100)];
    [path stroke];
}

6 . 弧形

  • 效果图


    Quartz 2D绘制路径、文字_第12张图片
    弧形
  • 主要使用方法

    //ArcCenter:所在圆的圆心
    //radius:半径
    //startAngle:开始角度;0度是在圆的最右侧;向上:度数是负的;向下:度数是正的
    //endAngle:截止角度(到哪个位置)
    //clockwise:是否为顺时针(怎么到达)
方法1:
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

方法2:
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);

//方法1:
- (void)drawRect:(CGRect)rect {
    CGPoint center = CGPointMake(rect.size.width/2.0, rect.size.height/2.0);
    CGFloat radius = rect.size.width/2.0 - 10;
    //思路:起点圆的最右侧;终点为-M_PI_2的位置(即向上90度的位置);从最右侧顺时针到达向上90度的位置
    UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:-M_PI_2 clockwise:YES];

    [path stroke]; 
}
//方法2:
- (void)drawRect:(CGRect)rect {
    CGPoint center = CGPointMake(rect.size.width/2.0, rect.size.height/2.0);
    CGFloat radius = rect.size.width/2.0 - 10;
    //这里调用方法不一样
    UIBezierPath * path = [UIBezierPath bezierPath];
    [path addArcWithCenter:center radius:radius startAngle:0 endAngle:-M_PI_2 clockwise:YES];
    [path stroke];
}

7. 扇形

  • 效果图


    Quartz 2D绘制路径、文字_第13张图片
    扇形效果图
  • 主要使用方法:

- (void)closePath;

思路:相当于是在弧上面添加两条直线到圆心,这样就形成了扇形

- (void)drawRect:(CGRect)rect {
    //画出弧
    CGPoint center = CGPointMake(rect.size.width/2.0, rect.size.height/2.0);
    CGFloat radius = rect.size.width/2.0 - 10;
    UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:-M_PI_2 clockwise:NO];
    
    //从弧上添加一条线到圆心
    [path addLineToPoint:center];
    
    //效果1--描边,我们添加一条线到圆心之后,需要把另外一条也加上,使用closePath方法即可:
    //关闭路径(从路径的终点连接一根到路径的起点)
    [path closePath];
    [path stroke];

    //效果2--填充,我们添加一条线之后,直接调用填充方法,另外一条线默认加上了
   // [path fill];
}

四.上下文状态

1.设置上下文状态

设置图形的上下文状态,可以改变绘图的颜色、宽度等。我们只是看其中一部分。


Quartz 2D绘制路径、文字_第14张图片
效果图
- (void)drawRect:(CGRect)rect {

   CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 50, 50)];
    
    //设置线条宽度
    CGContextSetLineWidth(ctx, 10);
    //设置棱角
    CGContextSetLineJoin(ctx, kCGLineJoinRound);
    //设置颜色
    [[UIColor yellowColor] set];
    
    CGContextAddPath(ctx, path.CGPath);
    
    CGContextStrokePath(ctx);
}
 

2.图形上下文状态栈

我们给上下文设置了状态,这种状态会运用到所有的路径当中。

例如,我们按照在添加两条直线,设置了一个状态,所有直线都会运用这个状态,就是图1的效果。

//制作两个不同颜色的路径
- (void)drawRect:(CGRect)rect {
    

    CGContextRef ctx = UIGraphicsGetCurrentContext();
 
    //第一条线 
    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(20, 150)];
    [path addLineToPoint:CGPointMake(280, 150)];
    CGContextAddPath(ctx, path.CGPath);
     
    CGContextSetLineWidth(ctx, 10);
    [[UIColor redColor] set];

    //第二条线 
    UIBezierPath * path2 = [UIBezierPath bezierPath];
    [path2 moveToPoint:CGPointMake(150, 20)];
    [path2 addLineToPoint:CGPointMake(150, 280)];
    CGContextAddPath(ctx, path2.CGPath);

    //取出上下文当中所有绘制的路径
    //把上下文当中的状态应用到所有路径中
    CGContextStrokePath(ctx);
 
}
Quartz 2D绘制路径、文字_第15张图片
效果图

现在我们想实现图2的效果。我们可以分开两次来绘制、渲染直线。也可以运用上下文栈,我们先用第一种方法:

同一个view上面设置不同的状态的路径--方法1:

- (void)drawRect:(CGRect)rect {
    //制作两个不同颜色的路径
    //----------------描绘第一个颜色的线--------------------

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(20, 150)];
    [path addLineToPoint:CGPointMake(280, 150)];
    CGContextAddPath(ctx, path.CGPath);
    //设置第一条路径的状态
    CGContextSetLineWidth(ctx, 10);
    [[UIColor redColor] set];
    
    //取出上下文当中所有绘制的路径
    //把上下文当中的状态应用到所有路径中
    CGContextStrokePath(ctx);
    
    
    //----------------绘制另一个颜色的线--------------------
    UIBezierPath * path2 = [UIBezierPath bezierPath];
    [path2 moveToPoint:CGPointMake(150, 20)];
    [path2 addLineToPoint:CGPointMake(150, 280)];
    CGContextAddPath(ctx, path2.CGPath);
  
    //设置第二条路径的状态
    CGContextSetLineWidth(ctx, 10);
    [[UIColor yellowColor] set];
    CGContextStrokePath(ctx);
    
}

同一个view上面设置不同的状态的路径--方法2(状态栈):

上下文里面,一部分用来保存路径,一部分保存状态;
还有一个上下文状态栈(先进后出),我们可以把状态保存到里面,需要的时候把状态取出来直接使用;


Quartz 2D绘制路径、文字_第16张图片
上下文状态栈
    //保存当前的状态栈 
    CGContextSaveGState(ctx);
  
     //从当前上下文状态栈中取出顶栈的状态,覆盖当前的上下文状态(取出来之后就不存放在栈里面了)
    CGContextRestoreGState(ctx);
- (void)drawRect:(CGRect)rect {
    
    //----------------描绘第一个颜色的线--------------------

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(20, 150)];
    [path addLineToPoint:CGPointMake(280, 150)];
    CGContextAddPath(ctx, path.CGPath);

    CGContextSetLineWidth(ctx, 20);
    [[UIColor yellowColor] set];

    //把当前黄色的上下文的状态保存到上下文状态栈(先进后出)
    CGContextSaveGState(ctx);
   
    CGContextSetLineWidth(ctx, 10);
    [[UIColor redColor] set];
    
    //取出上下文当中所有绘制的路径
    //把上下文当中的状态应用到所有路径中
    CGContextStrokePath(ctx);
    
    
    //----------------绘制另一个颜色的线--------------------
    UIBezierPath * path2 = [UIBezierPath bezierPath];
    [path2 moveToPoint:CGPointMake(150, 20)];
    [path2 addLineToPoint:CGPointMake(150, 280)];
    CGContextAddPath(ctx, path2.CGPath);

    //从上下文状态中取出状态
    //从当前上下文状态栈中取出顶栈的状态,覆盖当前的上下文状态(取出来之后就不存放在栈里面了)
    CGContextRestoreGState(ctx);

    CGContextStrokePath(ctx);
 
}

3.图形上下文的矩阵操作

 (void)drawRect:(CGRect)rect {
    
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 100)];
    [[UIColor redColor] set];
    
    //形变操作 x变化多少,y变化多少
    //平移
//    CGContextTranslateCTM(ctx, 0, 100);
    //缩放(放大的倍数)
    CGContextScaleCTM(ctx, 1.5, 1.5);
    //旋转
//    CGContextRotateCTM(ctx, M_PI_4);
    
    CGContextAddPath(ctx, path.CGPath);
        
    CGContextFillPath(ctx);

}

上面的矩阵操作可以图形进行放大、旋转,顺便补充一下可以达到同样效果的transform知识点:

//相对于原点进行操作
CGAffineTransformMakeScale(<#CGFloat sx#>, <#CGFloat sy#>)

//相对于某个点进行操作
CGAffineTransformScale(CGAffineTransform t,CGFloat sx, CGFloat sy)

注意:使用transform平移、旋转、缩放的话,避免两个同事使用,会错乱;

五.绘制文字

和上面所有操作一样,也是在view的draw里面进行;
我们给一个str进行绘制,不需要通过label就可以显示在view上面;

- (void)drawRect:(CGRect)rect {

    NSString * drawStr = @"Quartz 2D";
    
    [drawStr drawInRect:CGRectMake(10, 10, 200, 50) withAttributes:@{NSForegroundColorAttributeName:[UIColor purpleColor],
                                                                     NSStrokeColorAttributeName:[UIColor redColor],//描边
                                                                     NSFontAttributeName:[UIFont systemFontOfSize:25]}];
  
}

上面记录的是一些基本使用方法,后面会进行实例的记录。

你可能感兴趣的:(Quartz 2D绘制路径、文字)