Quartz 2D绘图 (1)初识

作者: mojue | 来源:发表于2016-05-04 00:16 被阅读315次

    Quartz 2D介绍

    • 什么是Quartz2D ?
      • Quartz 2D是⼀个二维绘图引擎,同时支持iOS和Mac系统。
    • Quartz 2D能完成的工作?
      • 绘制图形 : 线条\三角形\矩形\圆\弧形
      • 绘制文字
      • 绘制\生成图片(图像)
      • 读取\生成PDF
      • 截图\裁剪图片
      • 自定义UI控件
    • 其他需要了解的内容 ?
      • Quartz 2D是纯C语言的
      • Quartz 2D的API来自于CoreGraphics框架
      • 数据类型和函数基本都是CG作为前缀
        • CGContextRef
        • CGPathRef
        • CGContextStrokePath(ctx)
    • 使用Quartz2D绘图的基本步骤:
      • 自定义view
      • 重写drawRect方法
        • 1.获取图形上下文
        • 2.创建路径对象(添加路径)
        • 3.渲染

    绘制基本图形

    • 绘制一条线段:
      • 获取图形上下文
      • 移动到起点
      • 添加另一点
      • 渲染
      // 1.获取图形上下文
      CGContextRef cxtRef = UIGraphicsGetCurrentContext();
      
      // 2.添加路径
      // 起点
      CGContextMoveToPoint(cxtRef, 100, 80);
      // 另外一个点
      CGContextAddLineToPoint(cxtRef, 200, 200);
      
      // 3.渲染
      CGContextStrokePath(cxtRef); // 仅仅画线
      
    • 线段效果图</br>
    绘制线段.png
    • 绘制2条线段
    // 1.获取图形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    // 2.添加路径
    // 起点
    CGContextMoveToPoint(cxtRef, 50, 50);
    // 另外一个点
    CGContextAddLineToPoint(cxtRef, 200, 100);
    // 第2条线
    CGContextAddLineToPoint(cxtRef, 40, 180);
    // 3.渲染
    CGContextStrokePath(cxtRef);
    
    • 2条线效果图</br>
    绘制两条线段.png
    注意:CGContextStrokePath(cxtRef);只是进行画线,CGContextFillPath(cxtRef)会将线围起来的部分全部画出
    • 使用CGContextFillPath(cxtRef)的效果</br>
    fill的效果.png
    • 绘制三角形
    // 1.获取图形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    // 2.添加路径
    // 起点
    CGContextMoveToPoint(cxtRef, 50, 50);
    // 另外一个点
    CGContextAddLineToPoint(cxtRef, 200, 50);
    // 第2条线
    CGContextAddLineToPoint(cxtRef, 125, 180);
    // 再向起点连一条线
    CGContextAddLineToPoint(cxtRef, 50, 50);
    // 3.渲染
    CGContextStrokePath(cxtRef); // 仅仅画线
    
    • 三角形效果图</br>
    三角形.png

    绘制其他基本图形

    通过UIBezierPath绘制基本图形

    注意:如果在绘制一部分图形后仍需要绘制图形,必须将前面的路径关闭,再重新开始绘制。
    • 三角形及线段
      // 1.获取图形上下文
      CGContextRef cxtRef = UIGraphicsGetCurrentContext();
      
      // 2.图形路径
      UIBezierPath *bezierPath = [UIBezierPath bezierPath];
      [bezierPath moveToPoint:CGPointMake(10, 10)];
      [bezierPath addLineToPoint:CGPointMake(200, 200)];
      [bezierPath addLineToPoint:CGPointMake(10, 250)];
      [bezierPath addLineToPoint:CGPointMake(10, 10)];
      
      // 2.1如果需要再绘制其他的图形就必须先把当前的路径关闭
      [bezierPath closePath];
      
      // 2.2.再绘制一条线段
      [bezierPath moveToPoint:CGPointMake(250, 10)];
      [bezierPath addLineToPoint:CGPointMake(250, 290)];
      
      // 需要的是CGPathRef类型,可以通过.CGPath进行转换
      CGContextAddPath(cxtRef, bezierPath.CGPath);
      // 3.渲染
      CGContextDrawPath(cxtRef, kCGPathStroke);
      
    
    * 通过UIBezierPath绘制基本图形效果图</br>
    
    ![通过bezierPath绘制基本图形.png](https://img.haomeiwen.com/i1988000/57b79968c4183195.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    #### 绘制矩形
    ```objc
    // 1.获取图形上下文
    CGContextRef ctxRef = UIGraphicsGetCurrentContext();
    
    // 2.创建路径对象
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 200, 100)];
    
    // 3.将路径对象添加到"图形上下文"
    CGContextAddPath(ctxRef, path.CGPath);
    
    // 4.渲染
    CGContextDrawPath(ctxRef, kCGPathStroke);
    
    • 矩形效果图</br>
    绘制矩形.png

    绘制圆角矩形

    // 2.创建路径对象
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 10, 200, 100) cornerRadius:50];
    
    • 圆角矩形效果图</br>


      绘制圆角矩形.png

    绘制椭圆

    // 2.创建路径对象
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 200, 100)];
    
    • 椭圆效果图</br>
    绘制椭圆.png

    绘制圆弧

    • clockWise参数,代表绘制时YES:是逆时针绘制 NO:是顺时针绘制
    // 2.创建路径对象
    // 圆心
    CGPoint center = CGPointMake(150, 150);
    // 半径
    CGFloat radius = 100;
    // 开始角度
    CGFloat startAngle = 0;
    // 结束角度
    CGFloat endAngle = M_PI_2;
    // 是否为顺时针
    BOOL clokcWise = NO;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clokcWise];
    
    • 圆弧效果图</br>
    绘制圆弧.png

    绘制圆形

    // 圆心
    CGPoint center = CGPointMake(150, 150);
    // 半径
    CGFloat radius = 100;
    // 开始角度
    CGFloat startAngle = 0;
    // 结束角度
    CGFloat endAngle = M_PI * 2;
    // 是否为顺时针
    BOOL clokcWise = YES;
    return [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clokcWise];
    
    • 圆形效果图</br>
    绘制圆形.png

    绘制常见图形

    • 绘制扇形
    • 绘制饼状图
    • 绘制柱状图
    • 绘制下载进度条

    绘制扇形

    • 需要绘制弧形,指定起始角度及结束角度
    • 最后需要将弧形终点与圆心点连起来,再通过填充渲染就可以
      // 1.获取图形上下文
      CGContextRef cxtRef = UIGraphicsGetCurrentContext();
      
      // 2.创建弧形的路径对象
      // 圆心
      CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
      // 半径
      CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5;
      
      UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI_4 clockwise:YES];
      
      // 必须要将终点与圆形连线
      [path addLineToPoint:center];
      
      // 4.添加到图形上下文中
      CGContextAddPath(cxtRef, path.CGPath);
      
      // 5.渲染
      

    // CGContextDrawPath(cxtRef, kCGPathStroke);
    CGContextDrawPath(cxtRef, kCGPathFill);
    ```

    • 扇形效果图</br>


      绘制扇形.png

    绘制饼状图

    • 数组数据
    • 通过计算每个元素所占的比例
    • 获取图形上下文
    • 计算好圆心及半径
    • 遍历集合数据进行绘制
    • 计算每个扇形需要旋转的角度
    • 创建路径对象进行绘制
    注意:在绘制时,每个扇形的终止角度 = 上一个扇形的结束角度 + 当前扇形所需要的角度
    每次遍历最后都需要将扇形的起始角度置为上一个扇形的结束角度
    #pragma mark - 绘制饼状图
    - (void)drawRect:(CGRect)rect {
    
        // 1.数据
        NSArray *data = @[@25, @15, @10, @5, @30, @10, @5];
    
        // 2.遍历数据进行求和
        int total = 0;
        for (NSNumber *number in data) {
            float num = number.floatValue;
            total += num;
        }
        // 3.进行绘图操作
        // 3.1获取图形上下文
        CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    
        // 3.2遍历集合进行绘图操作
        // 圆心
        CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
        // 半径
        CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5;
    
        // 起始角度和结束角度
        // 注意:要在block内修改外部局部变量的值是必须要加__block进行修饰的
        __block CGFloat startAngle = 0;
        __block CGFloat endAngle = 0;
    
        // 遍历集合
        [data enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    
            // 转为float类型
            float num = obj.floatValue;
    
            // 计算单独一个数据结束角度,需要用它所占的角度 + 起始角度
            endAngle = (num / total) * (M_PI * 2) + startAngle;
    
            // 3.2.1创建路径对象
            UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
    
            // 3.2.2将弧线的终点与圆心连接起来
            [path addLineToPoint:center];
    
            // 3.2.3使用随机色进行渲染
            [[UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0] set];
    
            // 3.2.4添加到图形上下文中
            CGContextAddPath(cxtRef, path.CGPath);
    
            // 3.2.5渲染
            CGContextDrawPath(cxtRef, kCGPathFill);
    
            // 3.2.6修改起始角度
            startAngle = endAngle;
        }];
    
    
        // 4.创建同心圆遮盖中间部分
        UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius - 70 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
        [self.backgroundColor setFill];
    
        CGContextAddPath(cxtRef, arcPath.CGPath);
    
        // 渲染
        CGContextDrawPath(cxtRef, kCGPathFill);
    
    }
    
    
    • 饼状图效果图</br>
    绘制饼状图.png

    绘制柱状图

    • 计算所占比例
    • 获取图形上下文
    • 计算宽度,遍历集合进行绘制
    #pragma mark - 绘制柱状图
    - (void)drawRect:(CGRect)rect {
    
        // 1.数据
        NSArray *data = @[@100, @200, @300, @700, @79, @400];
    
        // 2.遍历数据进行求和
        float total = 0;
        for (NSNumber *number in data) {
            float num = number.floatValue;
            total += num;
        }
    
        // 3.进行绘图操作
        // 3.1获取图形上下文
        CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    
        // 3.2遍历集合进行绘图操作
        // 3.2.1计算宽度
        CGFloat width = rect.size.width / (data.count * 2 - 1);
    
        [data enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    
            // 计算高度
            CGFloat height = obj.floatValue / total * rect.size.height * 2;
    
            // 计算y
            CGFloat y = rect.size.height - height;
            // 计算x
            CGFloat x = (2 * width) * idx;
    
            // 创建路径对象
            UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, width, height)];
    
            // 添加到图形上下文中
            CGContextAddPath(cxtRef, path.CGPath);
    
            // 设置随机色
            [[UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0] set];
    
            // 渲染
            CGContextDrawPath(cxtRef, kCGPathFill);
        }];
    
    }
    
    • 柱状图效果图</br>
    绘制柱形图.png

    绘制下载进度条

    • 监听slider的滑动事件
    • 将slider的值传递给自定义view
    • 在自定义view中重写set方法,进行绘制
    • 绘制时弧线的结束角度根据传入的进度值进行计算
    #pragma mark - 绘制下载进度条
    - (void)drawRect:(CGRect)rect {
    
        // 1.获取上下文
        CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    
        // 2.创建路径对象
        // 圆心
        CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
        // 半径
        CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5;
        // 起点
        CGFloat startAngle = -M_PI_2;
        CGFloat endAngle = self.progress * M_PI * 2 - M_PI_2;
    
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
    
        [path addLineToPoint:center];
    
    #pragma mark - 创建一个圆形,利用奇偶填充模式留出中间的空白处
    //    UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius - 50 startAngle:startAngle endAngle:endAngle clockwise:YES];
    
    //    [path2 addLineToPoint:center];
    
        // 3.添加到上下文中
        CGContextAddPath(cxtRef, path.CGPath);
    //    CGContextAddPath(cxtRef, path2.CGPath);
        // 设置颜色
        [[UIColor brownColor] set];
    
        // 4.渲染
        CGContextDrawPath(cxtRef, kCGPathFill);
    //    CGContextDrawPath(cxtRef, kCGPathEOFill);
    }
    
    • 下载进度条图效果图</br>
    绘制下载进度条.gif

    图形上下文的矩阵操作

    注意: 红色框为上下文的边界

    旋转

    #pragma mark - 旋转 CTM
        // 注意:是绕着左上角进行转动
        CGContextRotateCTM(cxtRef, M_PI_4);
    
    • 旋转上下文</br>
    旋转.png

    缩放

    
    #pragma mark - 缩放 CTM
        // 注意:是以左上角为圆点缩放
        CGContextScaleCTM(cxtRef, 0.5, 0.5);
    
    • 缩小上下文</br>
    缩放.png

    平移

    #pragma mark - 平移
        CGContextTranslateCTM(cxtRef, 200, -100); 
    
    • 平移上下文</br>
    平移.png
    • 旋转缩放平移</br>
    旋转缩放平移.png

    图形上下文栈介绍

    • 在对图形上下文进行矩阵操作之前先将图像上下文最初的状态保存起来。保存的位置就是图形上下文的栈。
    • 在需要原始状态的时候进行再回复原始的状态的。
       
    #pragma mark - 矩阵操作
    - (void)drawRect:(CGRect)rect {
        
        //获取图形上下文
        CGContextRef cxtRef = UIGraphicsGetCurrentContext();
        //MARK: - 在矩阵操作前保存图形上下文
        CGContextSaveGState(cxtRef);
        
    #pragma mark - 旋转 CTM
        // 注意:是绕着左上角进行转动
        CGContextRotateCTM(cxtRef, M_PI_4);
        
    #pragma mark - 缩放 CTM
        // 注意:是以左上角为圆点缩放
        CGContextScaleCTM(cxtRef, 0.5, 0.5);
        
    #pragma mark - 平移
        CGContextTranslateCTM(cxtRef, 200, -100);  //为什么y值为0   和y值为-100   x值也会改变   缩放同时改变位置  位置会不准
        //创建路径
        //圆形
        UIBezierPath *roundPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(180, 150) radius:100 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
        //矩形
        UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(150, 280, 200, 100)];
        //直线
        UIBezierPath *linePath = [UIBezierPath bezierPath];
        [linePath moveToPoint:CGPointMake(50, 50)];
        [linePath addLineToPoint:CGPointMake(350, 400)];
        
        //添加路径
        CGContextAddPath(cxtRef, roundPath.CGPath);
        CGContextAddPath(cxtRef, rectPath.CGPath);
        CGContextAddPath(cxtRef, linePath.CGPath);
        
        //渲染
        CGContextDrawPath(cxtRef, kCGPathStroke);
        
        
        //MARK: - 在执行完毕矩阵操作以后,恢复上下文
        CGContextRestoreGState(cxtRef);
        
        //绘制与控制器视图大小相等的方框方便观察
        UIBezierPath *rectP = [UIBezierPath bezierPathWithRect:rect] ;
        //设置矩形的颜色
        [[UIColor redColor] setStroke];
        //设置矩形的线宽
        rectP.lineWidth = 10;
        
        [rectP stroke];
        
        
    }
    
    

    图形上下文的内存管理

    • CoreGraphics框架里面的使用到"create"和"copy","retain"函数创建的对象,最后都需要进行释放
    // 1.获取上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    
    // 2.创建路径对象
    CGMutablePathRef path = CGPathCreateMutable();
    
    
    // 起点
    CGPathMoveToPoint(path, NULL, 50, 50);
    
    // 另一点
    CGPathAddLineToPoint(path, NULL, 100, 100);
    
    
    // 3.将路径对象添加到图形上下文中
    CGContextAddPath(cxtRef, path);
    
    // 4.渲染
    CGContextDrawPath(cxtRef, kCGPathStroke);
    
    // 5.释放
    //    CGPathRelease(path);
    #pragma mark - CoreGraphics框架里面的使用到"create"和"copy","retain"函数创建的对象,最后都需要进行释放
    CFRelease(path);
    

    绘制图片和文字

    绘制图片

    //1.加载图片
         UIImage *mjImage = [UIImage imageNamed:@"头像"];
         //2.1方式1 通过一个点开始绘制
         [mjImage drawAtPoint:CGPointMake(50, 50)];
    
    通过一个点开始绘制.png
         //2.2方式2 通过某个区域绘制,可能会压缩或者拉伸图片,使图片不够美观
         [mjImage drawInRect:CGRectMake(50, 50, 100, 150)];
    
    通过某个区域绘制.png
    
         //2.3方式3 在某个区域内以平铺的方式绘制图片
         [mjImage drawAsPatternInRect:CGRectMake(0, 0, 300, 300)];
    
    在某个区域内以平铺的方式绘制图片.png

    绘制文字

    
    
    //字符串
        NSString *str = [NSString stringWithFormat:@"说好的不熬夜呢?"];
        
        NSDictionary *dic = @{
                              NSFontAttributeName : [UIFont systemFontOfSize:20],
                              NSForegroundColorAttributeName : [UIColor redColor]
                              };
        
        //方式1. 从某一个点开始绘制
        [str drawAtPoint:CGPointMake(50, 50) withAttributes:dic];
        
    
    
    从某一个点开始绘制.png
        //方式2. 在某一个区域进行绘制  (会自动换行,但是区域不要小于字体所占面积大小)
        [str drawInRect:CGRectMake(150, 150, 80, 150) withAttributes:dic];
    
    
    在某一个区域进行绘制.png

    相关文章

      网友评论

        本文标题:Quartz 2D绘图 (1)初识

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