美文网首页
Quartz 2D绘制路径、文字

Quartz 2D绘制路径、文字

作者: 蔚尼 | 来源:发表于2018-06-07 15:42 被阅读35次

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

    一. Quartz 2D简介:

    什么是Quartz 2D?

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

    Quartz 2D可以做的事情:

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

    例如:

    • 削圆


      削圆
    • 绘图

    • 做手势解锁


      手势解锁
    • 做折线图、饼状图、矩阵图(做复杂的我们可以使用Charts 这个第三方)

      饼状图

    二.图形上下文

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

    • 图形上下文(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
    图形上下文的理解

    三.路径绘制

    绘图顺序

    在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上面进行

    绘图基础

    总结:

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

    //直线
    //生成路径
    + (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.绘制直线

    • 效果图
      我们要绘制的效果,添加两条直线:
    直线效果图
    • 主要使用方法
    //生成路径
    + (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. 绘制曲线

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


      曲线效果图
    • 主要使用的这个方法:

    //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. 绘制矩形

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


      矩形效果图-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);
    
    矩形效果图- 2

    4.圆

    • 效果图


    • 主要使用方法
    //描述圆角矩形 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. 椭圆

    • 效果图


      椭圆的效果图
    • 主要使用方法

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

    6 . 弧形

    • 效果图


      弧形
    • 主要使用方法

        //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. 扇形

    • 效果图


      扇形效果图
    • 主要使用方法:

    - (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.设置上下文状态

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


    效果图
    - (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);
     
    }
    
    效果图

    现在我们想实现图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(状态栈):

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


    上下文状态栈
        //保存当前的状态栈 
        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绘制路径、文字

          本文链接:https://www.haomeiwen.com/subject/ejxisftx.html