iOS绘图系统简介
- UIKit:最常用的视图框架,如UIView、UIButton等UIKit元素
- Core Animation:提供强大的2D和3D动画效果
- OpenGL Embeded System/OpenGL:主要用于游戏绘制,但它是一套编程规范,具体由设备制造商实现
- Core Graphics:主要绘图系统,常用于绘制自定义视图,纯C的API,使用Quartz2D做引擎
- Graphics Hardware:GPU处理纹理叠加等图像渲染问题,相比较于CPU,浮点运算能力更强
*Core Image:给图片提供各种滤镜处理,比如高斯模糊、锐化等
绘图周期
iOS在运行循环(run loop)中整合所有的绘图请求,并一次将它们绘制出来。
不能在主线程中进行复杂的操作,否则会造成主线程的卡顿。
不能在主线程之外的主视图上下文中绘制,只要不是在主视图上下文中绘制,一些UIKit方法是可以在后台线程中使用的。比如可以在任意线程上使用CGBitmapCreateContext创建CGBitmapContext对象并在里面绘图。
1. 视图绘制
调用UIView中的drawRect方法。如果一个视图调用setNeedsDisplay方法,它就被标记为重新绘制,并且会在下一次绘图周期中重新绘制,也就是会自动调用drawRect方法。
2. 视图布局
调用UIView中的layoutSubviews方法。如果视图中的子视图布局发生变化,需要重新排列,UIKit会自动调用setNeedsLayout方法,也就是对于发生变化的视图逐层次调用layoutSubviews方法。比如frame发生变化、滚动视图等。
你不应该手动调用drawRect:方法!如果你想调用drawRect:方法更新视图,只需发送setNeedsDisplay方法。这将使得drawRect:方法会在下一个适当的时间调用。当然,不要覆盖drawRect:方法除非你知道这样做绝对合法。比方说,在UIImageView子类中覆盖drawRect:方法是不合法的,你将得不到你绘制的图形。
Context
Core Graphics API所有的操作都在上下文中进行,获得一个图形上下文是我们完成绘图任务的第一步,你可以将图形上下文理解为一块画布
- 两大绘图框架:UIKit和Core Graphics
- 三种获得图形上下文的方法:(drawRect:、drawRect: inContext:、UIGraphicsBeginImageContextWithOptions)
UIKit
像UIImage、NSString(绘制文本)、UIBezierPath(绘制形状)、UIColor都知道如何绘制自己。这些类提供了功能有限但使用方便的方法来让我们完成绘图任务。一般情况下,UIKit就是我们所需要的。
使用UiKit,你只能在当前上下文中绘图,所以如果你当前处于UIGraphicsBeginImageContextWithOptions函数或drawRect:方法中,你就可以直接使用UIKit提供的方法进行绘图。如果你持有一个context:参数,那么使用UIKit提供的方法之前,必须将该上下文参数转化为当前上下文。幸运的是,调用UIGraphicsPushContext 函数可以方便的将context:参数转化为当前上下文,记住最后别忘了调用UIGraphicsPopContext函数恢复上下文环境。
Core Graphics
这是一个绘图专用的API族,它经常被称为QuartZ或QuartZ 2D。Core Graphics是iOS上所有绘图功能的基石,包括UIKit。
使用Core Graphics之前需要指定一个用于绘图的图形上下文(CGContextRef),这个图形上下文会在每个绘图函数中都会被用到。如果你持有一个图形上下文context:参数,那么你等同于有了一个图形上下文,这个上下文也许就是你需要用来绘图的那个。如果你当前处于UIGraphicsBeginImageContextWithOptions函数或drawRect:方法中,并没有引用一个上下文。为了使用Core Graphics,你可以调用UIGraphicsGetCurrentContext函数获得当前的图形上下文。
6种绘图形式
- 在UIView的子类方法drawRect:中绘制一个蓝色圆
let path = UIBezierPath(ovalIn: CGRect(x: 100, y: 0, width: 100, height: 100))
UIColor.red.setFill()
path.fill()
- 在UIView的子类方法drawRect:中使用Core Graphics实现绘制蓝色圆
let context = UIGraphicsGetCurrentContext()
context?.addEllipse(in: CGRect(x: 0, y: 0, width: 100, height: 100))
context?.setFillColor(UIColor.blue.cgColor)
context?.fillPath()
- 在UIView子类的drawLayer:inContext:方法中实现绘图任务。drawLayer:inContext:方法是一个绘制图层内容的代理方法。为了能够调用drawLayer:inContext:方法,我们需要设定图层的代理对象。但要注意,不应该将UIView对象设置为显示层的委托对象,这是因为UIView对象已经是隐式层的代理对象,再将它设置为另一个层的委托对象就会出问题。轻量级的做法是:编写负责绘图形的代理类
- 使用Core Graphics在drawLayer:inContext:方法中实现
- 使用UIKit实现
UIGraphicsBeginImageContextWithOptions(CGSize(width: 100, height: 100), true, 1)
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 100, height: 100))
UIColor.orange.setFill()
path.fill()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
- 使用Core Graphics实现
UIGraphicsBeginImageContextWithOptions(CGSize(width: 100, height: 100), true, 0)
let context = UIGraphicsGetCurrentContext()
context?.addEllipse(in: CGRect(x: 0, y: 0, width: 100, height: 100))
context?.setFillColor(UIColor.blue.cgColor)
context?.fillPath()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImage常用的绘图操作
Core Graphics坐标系统
- UIKit点坐标系
- Core Grphics坐标系
Core Graphics源于Mac OS X系统,在Mac OS X中,坐标原点在左下方并且正y坐标是朝上的,而在iOS中,原点坐标是在左上方并且正y坐标是朝下的。在大多数情况下,这不会出现任何问题,因为图形上下文的坐标系统是会自动调节补偿的。但是创建和绘制一个CGImage对象时就会暴露出倒置问题。 - 单位坐标系
透明、不透明、隐藏
视图上有三个比较容易混淆的属性:alpha(透明)、opaque(不透明)和hidden(隐藏)。下面就进行一下深入的区分。
alpha属性决定了视图会通过像素显示多少信息。alpha为1表示所有的视图信息都在像素上表现出来,而alpha为0表示没有视图信息能在像素上表示出来。
opaque,将视图标记为opaque,便是向绘图系统许诺即将绘制的每一个像素都要使用全不透明的颜色,这便允许绘图系统忽略被覆盖在下面的视图,这样可以改善性能,尤其是在进行变形的时候。与opaque紧密相关的是clearsContextBeforeDrawing。它的默认值是YES,会在调用drawRect之前将上下问设置为透明黑底。这样会避免视图中产生的任何垃圾数据。
hidden为YES的话,表示视图根本不会被绘制。
隐藏和透明视图不接受触摸事件,如果想创建一个透明视图并且接受触摸事件,可以这样来进行设置,设置它的alpha为1、opaque为NO且backgroundColor为你活[UIColor clearColor],来接手触摸事件。