一、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 画图片
- CGContextStrokePath
绘制基本图形
查阅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函数必须成对出现,否则绘图很可能出现意想不到的错误