iOS Quartz 2D绘图知识详解

作者: LiYaoPeng | 来源:发表于2017-01-15 17:37 被阅读422次

    ** Quartz**

    需要注意的是在UIImageView的子类中重写drawRect:是非法的,你不能把自己的绘制和UIImageView进行结合。

    **1. ** Mac OS X的Darwin核心之上的绘图层,有时候也认为是CoreGraphics。共有两种部分组成Quartz:
    **2. ** Quartz Compositor,合成视窗系统,管理和合成幕后视窗影像来建立Mac OS X使用者接口。
    3. Quartz 2D,是iOS和Mac OS X环境下的二维绘图引擎。
    涉及内容包括:基于路径的绘图,透明度绘图,遮盖,阴影,透明层,颜色管理,防锯齿渲染,生成PDF,以及PDF元数据相关处理。


    ****一、Quartz 2D的简单介绍*****
    **1. ** Quartz 2D属于Core Graphics(所以大多数相关方法的都是以CG开头),是iOS/Mac OSX 提供的在内核之上的强大的2D绘图引擎,并且这个绘图引擎是设备无关的。也就是说,不用关心设备的大小,设备的分辨率,只要利用Quartz 2D,这些设备相关的会自动处理。
    2.Quartz 2D能够提供的强大功能如下:

    1. 透明层(transparency layers) 
    2. 阴影 基于path的绘图(path-based drawing)
    3. 离屏渲染(offscreen rendering) 
    4. 复杂的颜色处理(advanced color management) 
    5. 抗锯齿渲染(anti-aliased rendering)
    6. PDF创建,展示,解析(这部分不在这个系列之中) 配合Core Animation, OpenGL ES,UIKit完成复杂的功能 画板-The Graphics Context
    7. 而Quartz 2D的容器就是CGContextRef数据模型。这种数据模型是C的结构体,存储了渲染到屏幕上需要的一切信息。
    

    二、Quartz 2D详解:

    Quartz 2D的基本数据类型:
    Quartz 2D中的数据类型都是透明的,也就是说用户只需要使用即可,不需要实际访问其中的变量。具体的数据类型包括

    1. CGPathRef 路径类型,用来绘制路径(注意带有ref后缀的一般都是绘制的画板)
    2. CGImageRef,绘制bitmap
    3. CGLayerRef,绘制layer,layer可复用,可离屏渲染
    4. CGPatternRef,重复绘制
    5. CGShadingRef和CGGradientRef,绘制渐变(例如颜色渐变)
    6. CGFunctionRef,定义回调函数,CGShadingRef和CGGradientRef的辅助类型
    7. CGColorRef and CGColorSpaceRef,定义如何处理颜色
    8. CGFontRef,绘制文字
    

    Quartz 2D的坐标

    UIKit默认的坐标系统与Quartz不同。在UIKit中,原点位于左上角,y轴正方向为向下。UIView通过将修改Quartz的Graphics Context的CTM[原点平移到左下角,同时将y轴反转(y值乘以-1)]以使其与UIView匹配。这些都是系统自动帮我们完成。

    三、直线/矩形

    1. 基本图形绘制需要的属性
    1.获取当前上下文(context)(UIGraphicsGetCurrentContext)
    2.设置颜色:
        CGContextSetFillColorWithColor:设置描边颜色
        CGContextSetFillColorWithColor:设置填充颜色
    3. 画的范围
        CGContextStrokeRect:描边的范围
        CGContextFillRect:填充的范围
    4.CGContextSetLineWidth:线宽
    5.CGContextSetLineCap:线顶端的样式
    6.CGContextSetLineJoin:线拐角的样式
    7. 线的起始点:
        CGContextMoveToPoint:起点 
        CGContextAddLineToPoint:终点
    8.CGContextFillPath :填充的路径
    9.CGContextStrokePath:描边的路径
    

    直线、矩形 demo

    - (void)drawRect:(CGRect)rect {
        
        //1.获得当前context
        CGContextRef context = UIGraphicsGetCurrentContext();
        
        //设置颜色 (填充色和 描边的颜色)
        CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:0.8 alpha:1].CGColor);
        CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
        
        //设置描边线宽
        CGContextSetLineWidth(context, 20);
        
        //对矩形进行填色  或  描边
        //(注意:如果先描边再填充,由于矩形大小一致,那么描边的线就会被填充的矩形挡住)
        CGContextFillRect(context, rect);
        CGContextStrokeRect(context, rect);
        
        //-----------------------------------------------------------------
        
        //MARK: ------ 实际line和point的代码
        // 设置描边颜色
        CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);    CGContextSetLineWidth(context, 8.0);//线的宽度
        CGContextSetLineCap(context, kCGLineCapRound);//线的顶端
        CGContextSetLineJoin(context, kCGLineJoinRound);//线相交的模式
        
        //-----------------------------------------------------------------
        //MARK:黄色的  ">" 图形
        //移动画笔到哪个点
        CGContextMoveToPoint(context,20,20);
        //画笔画到哪个点
        CGContextAddLineToPoint(context, rect.size.width - 20, rect.size.height / 2 - 20);
        CGContextAddLineToPoint(context, 20, rect.size.height - 20);
        //根据上下文中的点,成线进行描边
        CGContextStrokePath(context);
        
        //------------------------------------------------------------------
        //MARK: 红色的小的三角的填充
        CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
        CGContextMoveToPoint(context, 0, rect.size.height / 2 - 30);
        CGContextAddLineToPoint(context, 30, rect.size.height / 2);
        CGContextAddLineToPoint(context, 0, rect.size.height / 2 + 30);
        CGContextFillPath(context);
       //虚线效果
      //CGContextSetLineDash(context, 1, lengths, 1);
    
     //------------------------------------------------------------------
        //MARK: 红色虚线效果
        CGContextSetStrokeColorWithColor(context,[UIColor redColor].CGColor);
        CGContextSetLineWidth(context, 1);
        
        CGContextMoveToPoint(context, rect.size.width - 20, 20);
        CGContextAddLineToPoint(context, rect.size.height - 20, rect.size.width - 20);
        CGFloat lengths[] = {20};
        CGContextSetLineDash(context, 1, lengths, 1);
        CGContextStrokePath(context);
    }
    

    运行之后的效果:

    虚线效果

    CGContextSetLineDash参数详解
    void CGContextSetLineDash (
    CGContextRef _Nullable c,
    CGFloat phase,
    const CGFloat * _Nullable lengths,
    size_t count
    );
    c 绘制的context,这个不用多说
    phase,第一个虚线段从哪里开始,例如传入3,则从第三个单位开始
    lengths,一个C数组,表示绘制部分和空白部分的分配。例如传入[2,2],则绘制2个单位,然后空白两个单位,以此重复
    count lengths的数量


    四、曲线— 圆弧的绘制

    Quartz提供了两个方法来绘制圆弧

    1. CGContextAddArc,普通的圆弧一部分(以某圆心,某半径,某弧度的圆弧)

    2. CGContextAddArcToPoint,用来绘制圆角

    3. CGContextAddArc

    1. 结构:
      void CGContextAddArc (
      CGContextRef _Nullable c,
      CGFloat x, // 圆心X坐标
      CGFloat y, // 圆心Y坐标
      CGFloat radius, // 弧度半径
      CGFloat startAngle, // 开始的弧度
      CGFloat endAngle, // 结束的弧度
      int clockwise //1表示顺时针,0表示逆时针
      );
    - (void)drawRect:(CGRect)rect {
        //--------------------------------------------------------------------
        //MARK: 画弧
        //1.获取图片上下文
        CGContextRef context = UIGraphicsGetCurrentContext();
        //2.设置弧度及位置
         //根据中心点,半径,起始的弧度,最后的弧度,是否顺时针画一个圆弧
        CGContextAddArc(context, rect.size.width / 2, rect.size.height / 2, 20, M_PI_4, M_PI, 1);
        CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
        //3.画
        CGContextDrawPath(context, kCGPathStroke);
    
        // -----------------------------------------------------
    
        //MARK:画有线圈的圆饼
        CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);//设置线的颜色
        CGContextSetRGBFillColor(context, 0, 0, 1, 1);//设置填充颜色
        CGContextSetLineWidth(context, 2); //设置线的宽度
        CGContextAddEllipseInRect(context, CGRectMake(10, 30, 60, 60)); //画一个椭圆或者圆
        CGContextDrawPath(context, kCGPathFillStroke);
    }
    

    2.CGContextAddArcToPoint

    void CGContextAddArcToPoint (
    CGContextRef _Nullable c,
    CGFloat x1,
    CGFloat y1,
    CGFloat x2,
    CGFloat y2,
    CGFloat radius
    );
    c context x1,y1和当前点(x0,y0)决定了第一条切线(x0,y0)->(x1,y1) x2,y2和(x1,y1)决定了第二条切线 radius,想切的半径。
    也就是说,
    绘制一个半径为radius的圆弧,和上述 两条直线都相切。

    - (void)drawRect:(CGRect)rect {
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);//设置线的颜色
        CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);//设置填充颜色
        CGContextSetLineWidth(ctx, 2); //设置线的宽度
    
        //CGContextAddArcToPoint 先要确定三个点,
        //1.从哪里开始划线 CGContextMoveToPoint (也就是两条线的交点)
        //2.第二个点与起始点  确定一条直线
        //3.第三个点与第二个点  确定另外一条直线
        
        //画一个圆角矩形
        //确定矩形的位置和大小
        CGRect rrect = CGRectMake(rect.size.width / 2 - 30, rect.size.height / 2 - 30, 60.0, 60.0);
        
        CGFloat radius = 15.0;//半径,半径为正方形一半时,那就可以切成圆形
        
        CGFloat
        minx = CGRectGetMinX(rrect),//矩形中最小的x
        midx = CGRectGetMidX(rrect),//矩形中最大x值的一半
        maxx = CGRectGetMaxX(rrect);//矩形中最大的x值
        
        CGFloat
        miny = CGRectGetMinY(rrect),//矩形中最小的Y值
        midy = CGRectGetMidY(rrect),//矩形中最大Y值的一半
        maxy = CGRectGetMaxY(rrect);//矩形中最大的Y值
        
        
        CGContextMoveToPoint(ctx, minx, midy);//从点A 开始
        //从点A到点B再从点B到点C形成夹角进行切圆
        CGContextAddArcToPoint(ctx, minx, miny, midx, miny, radius);
        CGContextAddArcToPoint(ctx, maxx, miny, maxx, midy, radius);
        CGContextAddArcToPoint(ctx, maxx, maxy, midx, maxy, radius);
        CGContextAddArcToPoint(ctx, minx, maxy, minx, midy, radius);
        CGContextClosePath(ctx);
        CGContextDrawPath(ctx, kCGPathFillStroke);
        //如果想要进行裁切的话去掉CGContextDrawPath(ctx, kCGPathFillStroke);方法 添加以下方法
        //CGContextClip(context);
        //CGContextFillPath(context);
    //添加图片
    //CGContextDrawImage(context, rect, self.image.CGImage);    
    //或者[self.image drawInRect:rect];
    }
    
    

    运行效果:

    贝塞尔曲线

    - (void)drawRect:(CGRect)rect {
        
        CGContextRef context = UIGraphicsGetCurrentContext();
        
        CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
        CGContextSetLineWidth(context, 4);
       
        
        CGFloat
        minx = CGRectGetMinX(rect),//矩形中最小的x
        midx = CGRectGetMidX(rect),//矩形中最大x值的一半
        maxx = CGRectGetMaxX(rect);//矩形中最大的x值
        
        CGFloat
        miny = CGRectGetMinY(rect),//矩形中最小的Y值
        midy = CGRectGetMidY(rect),//矩形中最大Y值的一半
        maxy = CGRectGetMaxY(rect);//矩形中最大的Y值
        
        //贝塞尔曲线一,两个控制点 红色
        CGPoint s = CGPointMake(minx + 10, miny + 10); //起始点
        CGPoint e = CGPointMake(maxx - 10, maxy - 10);//终点
        CGPoint cp1 = CGPointMake(miny, midy);//控制点1
        CGPoint cp2 = CGPointMake(midy, minx);//控制点2
        CGContextMoveToPoint(context, s.x, s.y);
        CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
        CGContextStrokePath(context);
        
        //贝塞尔曲线二,一个控制点 蓝色
        CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
        s = CGPointMake(minx, maxy);
        e = CGPointMake(maxx, maxy);
        cp1 = CGPointMake(midx, midy);
        CGContextMoveToPoint(context, s.x, s.y);
        CGContextAddQuadCurveToPoint(context, cp1.x, cp1.y, e.x, e.y);
        CGContextStrokePath(context);
    }
    

    运行效果

    五、颜色渐变
    demo

    - (void)drawRect:(CGRect)rect {
        // Drawing code
        
        // 创建Quartz上下文
        CGContextRef context = UIGraphicsGetCurrentContext();
        
        // 创建色彩空间对象
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        
        // 创建起点颜色
        CGColorRef beginColor = CGColorCreate(colorSpaceRef, (CGFloat[]){0.01f, 0.5f, 0.01f, 1.0f});
        
        // 创建终点颜色
        CGColorRef endColor = CGColorCreate(colorSpaceRef, (CGFloat[]){0.99f, 0.99f, 0.01f, 1.0f});
        
        // 创建颜色数组
        const void **values = (const void*[]){beginColor, endColor};//颜色数组
        CFArrayRef colorArray = CFArrayCreate(
                                              kCFAllocatorDefault,
                                              values,//颜色数组
                                              2,//数组的个数
                                              nil// CGGradientCreateWithColors的最后一个locations参数可以传空,这样默认为从0.0到1.0。
                                              );
        
        // 创建渐变对象
        CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpaceRef, colorArray, (CGFloat[]){
            0.0f,       // 对应起点颜色位置
            1.0f        // 对应终点颜色位置
        });
        
        // 释放颜色数组
        CFRelease(colorArray);
        
        // 释放起点和终点颜色
        CGColorRelease(beginColor);
        CGColorRelease(endColor);
        
        // 释放色彩空间
        CGColorSpaceRelease(colorSpaceRef);
        
        /*
         1.context          上线文
         2.gradientRef      颜色数组
         3.startPoint       开始位置
         4.endPoint         结束位置
         5.CGGradientDrawingOptions         
         当你的起点或者终点不在图形上下文的边缘内时,指定该如何处理。你可以使用你的开始或结束颜色来填充渐变以外的空间。此参数为以下值之一:
         KCGGradientDrawsAfterEndLocation扩展整个渐变到渐变的终点之后的所有点 
         KCGGradientDrawsBeforeStartLocation扩展整个渐变到渐变的起点之前的所有点。
         0不扩展该渐变。
         */
        CGPoint startPoint = CGPointMake(0.0f, 0.0f);
        CGPoint endPoint = CGPointMake(rect.size.width, rect.size.height);
        CGGradientDrawingOptions options = kCGGradientDrawsAfterEndLocation;
        CGContextDrawLinearGradient(
                                    context,
                                    gradientRef,
                                    startPoint,
                                    endPoint,
                                    kCGGradientDrawsBeforeStartLocation
    //                                kCGGradientDrawsAfterEndLocation
                                    );
        
        
        // 释放渐变对象
        CGGradientRelease(gradientRef);
    }
    
    

    效果图


    六、多中颜色的渲染
    demo

    - (void)drawRect:(CGRect)rect {
        // 创建Quartz上下文
        CGContextRef context = UIGraphicsGetCurrentContext();
        
        // 创建色彩空间对象
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        
        // 创建渐变对象
        CGGradientRef gradientRef =
        CGGradientCreateWithColorComponents(colorSpaceRef,
                                            (CGFloat[]){
                                             1.0f,0.8f,0.5f,1.0f,//第一个颜色RGB 和透明度
                                             0.6f,0.5f,0.6f,1.0f,//第二个颜色RGB 和透明度
                                             0.3f,0.2f,0.f,1.0f,//第三个颜色RGB 和透明度
                                             .0f,0.0f,0.3f,1.0f
                                            },
                                            (CGFloat[]){  0.0f,0.3f,.6f,1},//颜色渐变的位置
                                            4);//颜色的个数
        
        // 释放色彩空间
        CGColorSpaceRelease(colorSpaceRef);
        
        // 填充渐变色
        CGContextDrawLinearGradient(context, gradientRef, CGPointMake(0.0f, 0.0f), CGPointMake(320.0f, 460.0f), 0);
        
        // 释放渐变对象
        CGGradientRelease(gradientRef);
    }
    

    效果


    此外还可以绘制文字

    //绘制文字(不用开启图形上下文)
        NSString *str = @“hehe呵呵hehe🙄";
        CGRect wordRect = CGRectMake(100, 100, 100, 40);
        //给要显示的文字设置一个范围
        //NSForegroundColorAttributeName:文字颜色
        //NSFontAttributeName : 字体
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        //设置字体颜色
        dict[NSForegroundColorAttributeName] = [UIColor greenColor];
        //设置字体大小
        dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
        [str drawInRect:wordRect withAttributes:dict];
    

    还可以截屏操作

     // 1.开启上下文
        UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
        
        // 2.将控制器view的layer渲染到上下文
        [view.layer renderInContext:UIGraphicsGetCurrentContext()];
        
        // 3.取出图片
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        
        // 4.结束上下文
        UIGraphicsEndImageContext();
    

    几何图形绘制属性---详解
    1.Graphics Context(图层上下文)

    1. Graphics Context
      其实就是表示了一个绘制目标,也就是你打算绘制的地方,它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息。Graphics Context定义了基本的绘制属性,如颜色、裁减区域、线条宽度和样式信息、字体信息、混合模式等。
    1. *获取Graphics Context:
      1. Quartz提供的创建函数、Mac OS X框架或IOS的UIKit框架提供的函数。Quartz提供了多种Graphics Context的创建函数,包括bitmap和PDF,我们可以使用这些Graphics Context创建自定义的内容。
      2. 在代码中,我们用CGContextRef来表示一个Graphics Context。当获得一个Graphics Context后,可以使用Quartz 2D函数在上下文(context)中进行绘制、完成操作(如平移)、修改图形状态参数(如线宽和填充颜色)等。

    查阅的资料
    感谢大家在博客或者简书的分享,我在这里做了总结和扩展,仅供大家学习和讨论,如果有什么不对的请大家及时留言评论,谢谢~
    本人github账号: 702029772@qq.com

    1. 博主:zenny_chen http://www.cnblogs.com/zenny-chen/archive/2012/02/23/2364152.html
    1. 博主:贱见 https://account.aliyun.com/login/login.htm?from_type=yqclub&oauth_callback=https%3A%2F%2Fyq.aliyun.com%2Fusers%2F1482959332945947%3Fspm%3D5176.100239.blogrightarea35932.3.0TK01t%26do%3Dlogin

    相关文章

      网友评论

      • 开飞机的叔客:有质量的一篇学习
      • Lackaday:帅呆了~(¯﹃¯)
      • 45f3f7bab550:可以,总结很全。
      • 3514f1a3cacc:真得很实用哦
      • 小热带雨林:跪谢楼主分享这么详细的讲解,很是喜欢这种知识点的总结,谢谢分享,已关注,话说,李总什么时候开公司啊?
        小热带雨林:@LiYaoPeng 开玩笑,那是一定的,就怕去帮忙的时候,李总不要啊!
        LiYaoPeng:@哇噻_小黄人 正准备呢,到时候来帮忙管理吧~
      • best_su:非常全面,非常完美
        LiYaoPeng:@一路Y前行
        谢谢~ 如果有什么不对的地方,请及时指正~
      • Freakthen:再次感谢作者,解决我很大的问题。这东西已经困扰我很久了,如今我茅塞顿开。感谢作者。已经关注。
        LiYaoPeng:@Freakthen 谢谢~ 如果有什么不对的地方,请及时指正~
      • Freakthen:很久没有看见总结这么好的文章了,很全面。很好用。
      • LiYaoPeng:感谢大家在博客或者简书的分享,我在这里做了总结和扩展,仅供大家学习和讨论,如果有什么不对的请大家及时留言评论,谢谢~

      本文标题: iOS Quartz 2D绘图知识详解

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