Quartz 2D绘图详解

一、iOS中的绘图

Quartz 2D && Core Graphics

在iOS中的绘图框架就是Quartz 2D,Quartz 2D基于Core Graphics框架的,是一个强大的二维图像绘制引擎。Core Graphics在UIKit中也有很好的封装和集成,我们日常用到的UIKit组件都是由Core Graphics进行绘制的。不仅如此,当我们引入UIKit框架时会自动引入Core Graphics框架,并且为了方便开发者使用,在UIKit内部还对一些常用的绘图API进行了封装。

图形上下文

绘制需要一个图形上下文来保存用户绘制的内容状态(颜色、填充色等) 和 决定绘制到哪个地方(绘制路径).

什么是图形上下文

  • 用户把绘制好的内容先保存到图形上下文,
  • 然后根据选择的图形上下文的不同,绘制的内容显示到地方也不相同,即输出目标也不相同.

图形上下文的类型有哪些

  • Bitmap Graphics Context(位图上下文)
  • PDF Graphics Context
  • Window Graphics Context
  • Layer Graphics Context(图层上下文,自定义UIView取得上下文就是图层上下文.UIView之所以能够显示就是因为他内部有一个图层)
  • Printer Graphics Context

如何绘制,在哪儿绘制

绘制步骤

  • 首先得要有上下文,有了上下文才能决定把绘制的东西显示到哪个地方去.
  • 其次就是这个上下文必须得和View相关联.才能将内容绘制到View上面.
  • 步骤:
    • 要先自定义UIView
    • 实现 DrawRect 方法
    • DrawRect 方法中取得跟View相关联的上下文.
    • 设置上下文状态
    • 绘制路径(描述路径长什么样).
    • 把描述好的路径保存到上下文(即:添加路径到上下文)
    • 把上下文的内容渲染到View

DrawRect方法

  • 在绘制时,只有在 DrawRect 方法当中才能取得和view相关联的图形上下文
  • DrawRect 是系统自己调用的, 它是当View显示的时候自动调用。如果需要重绘,不能直接调用 DrawRect,需要调用 setNeedsDisplay 或者 setNeedsDisplayInRect 触发 DrawRect
  • 通过设置contentMode属性值为UIViewContentModeRedraw,那么将在每次设置或更改frame的时候自动调用 DrawRect 方法重绘
  • UIImageView子类重写 DrawRect 方法无效,因为UIImageView是专门为显示图片做的控件,用了最优显示技术,不让调用 DrawRect 方法,如果需要绘制图片,使用UIView进行绘制

二、基础绘制

参照Quartz Demo

绘制基本流程

  • 获取上下文
    • UIGraphicsGetCurrentContext()
  • 设置上下文状态
    • CGContextSetRGBStrokeColor 设置画笔颜色
    • CGContextSetRGBFillColor 设置填充颜色
    • CGContextSetLineWidth 设置线宽
    • CGContextSetLineCap 设置边帽
  • 设置绘制路径
    • CGContextMoveToPoint 画笔移动至某点
    • CGContextAddLineToPoint 添加一条线至某点
    • CGContextAddRect 添加一个矩形
    • CGContextAddEllipseInRect 根据矩形添加圆
    • CGContextAddArc 添加一段弧线
    • CGContextAddCurveToPoint 画一段曲线
    • CGContextAddPath 添加一个路径
  • 绘制路径至上下文
    • CGContextStrokePath
    • CGContextFillPath 填充路径
    • CGContextDrawImage 画图片

绘制基本图形

查阅QuartzDemo 实现以下

  • 画线
  • 画矩形
  • 画圆形
  • 画弧形
  • 贝塞尔曲线(了解贝塞尔曲线)

三、高级绘制

  • 图像(坐标系调整)
    • 之前绘制其他东西没有问题因为系统帮我们把坐标校正了,其实Quartz 2D的内容坐标原点,在左下角
    • 需要调整坐标系
  • 画线(动态调整线宽)
    • 根据UISlider调整线宽
    • 只能放大不能缩小bug 原因是代码创建的UIView的backgroundColor默认值为nil
    • clearsContextBeforeDrawing属性说明:在绘制前清空上下文,如果opaque为YES,则backgroundColor属性不能为nil
  • 画两条线(图形上下文状态栈)

四、UIKit绘制

UIBezierPath

  • 画线

  • 画矩形或圆

  • 饼状图

    • 画弧线
  • 文字

    • drawAtPoint 不会换行
    • drawInRect 指定宽度可以换行
  • 给图片添加水印

    • UIGraphicsBeginImageContextWithOptions开启一个图片大小的图片上下文
    • 将图片绘制上去drawAtPoint
    • 将文字绘制上去drawAtPoint
    • UIGraphicsGetImageFromCurrentImageContext 从当前图片上下文生成一张图片
    • UIGraphicsEndImageContext关闭图片上下文
  • 简单图形图片裁剪

    • UIGraphicsBeginImageContextWithOptions 开启一个图片大小的图片上下文
    • 绘制一个圆形 [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)]
    • 把圆形设置为裁剪区域,超出区域外的统统裁剪 [path addClip]
    • 绘制图片 drawAtPoint
    • UIGraphicsGetImageFromCurrentImageContext 获取裁剪后的图片
    • 关闭上下文
  • 截屏

    • 开启一个图片上下文
    • 获取当前上下文
    • 将当前根view的layer绘制到上下文中 [self.view.layer renderInContext:ctx]
    • 生成图片 UIGraphicsGetImageFromCurrentImageContext
    • 关闭图片上下文
  • 图片擦除(抽奖)

    • 两张图片叠加,上面的图片添加pan手势
    • locationInView获取当前手指的点
    • 生成擦除区域
    • 开启图片上下文
    • 获取当前上下文,并且把noImageview的内容渲染到当前上下文
    • 擦除指定区域 CGContextClearRect
    • 从当前图片上下文取出图片
    • 关闭图片上下文
    • 设置图片

五、其他

第三方库

CorePlot:https://github.com/core-plot/core-plot

坐标系转换

上下文的矩阵操作其实就是修改上下文的形变

主要有以下几种

  • 平移:CGContextTranslateCTM(ctx, 100, 100);

  • 旋转:CGContextRotateCTM(ctx, M_2_PI);

  • 缩放:CGContextScaleCTM(ctx, 0.5, 0.5);

    注意:上下文操作必须得要在添加路径之前去设置

上下文状态栈

CGContextSaveState和CGContextRestoreState函数
当你在图形上下文中绘图时,当前图形上下文的相关属性设置将决定绘图的行为与外观。因此,绘图的一般过程是先设定好图形上下文参数,然后绘图。比方说,要画一根红线,接着画一根蓝线。那么首先需要将上下文的线条颜色属性设定为为红色,然后画红线;接着设置上下文的线条颜色属性为蓝色,再画出蓝线。表面上看,红线和蓝线是分开的,但事实上,在你画每一条线时,线条颜色却是整个上下文的属性。无论你用的是UIKit方法还是Core Graphics函数。
因为图形上下文在每一时刻都有一个确定的状态,该状态概括了图形上下文所有属性的设置。为了便于操作这些状态,图形上下文提供了一个用来持有状态的栈。调用CGContextSaveState函数,上下文会将完整的当前状态压入栈顶;调用CGContextRestoreState函数,上下文查找处在栈顶的状态,并设置当前上下文状态为栈顶状态。

因此一般绘图模式是:在绘图之前调用CGContextSaveState函数保存当前状态,接着根据需要设置某些上下文状态,然后绘图,最后调用CGContextRestoreState函数将当前状态恢复到绘图之前的状态。要注意的是,CGContextSaveState函数和CGContextRestoreState函数必须成对出现,否则绘图很可能出现意想不到的错误

posted @ 2016-12-15 10:56  听歌学唱  阅读(161)  评论(0编辑  收藏  举报