在日常的 iOS 开发中,我们除了会直接使用由系统框架提供的 UI 组件以外,也会自定义各种不同的 UI 组件。有各种各样不同的方式来实现自定义的 UI 组件,其中使用系统的绘图 API 来绘制图形是比较常见的一种方式。
Graphics Contexts
Graphics Contexts 可以理解为一块画布,所以绘图相关的操作都是在它上面进行的。
那么应该怎么创建或者获取 Graphics Contexts 呢?Graphics Contexts 的来源一般有两种:1) 通过系统获取,2) 自行创建。
获取 Graphics Contexts
通过系统获取 Graphics Contexts
一般,在以下场景中,我们能够之间从系统获取 Graphics Contexts:
drawRect:
drawInContext:
drawLayer:inContext:
我们可以通过创建 UIView
的子类来重写 drawRect:
方法,在该方法中,我们已经处于一个 Graphics Contexts 中,之间在其上绘制就好。
drawInContext:
和 drawLayer:inContext:
都和 CALayer
有关,drawInContext:
是 CALayer
的一个实例方法,drawLayer:inContext:
是 CALayer
的代理方法(UIView
是嵌有一个 CALayer
的,也是该 CALayer
的代理)。我们可以通过创建 CALayer
的子类或者成为 CALayer
的代理来实现上述两个方法,然后完成绘制的操作。在这两个方法中,都有一个 Graphics Contexts,可以在其上绘制,与 drawRect:
不同的是,传入的 Graphics Contexts 并不是当前的 Graphics Contexts。(⚠️:在实践中发现,如果使用 drawInContext:
和 drawLayer:inContext:
来绘制,需要对 CALayer
调用 setNeedsDisplay
方法,否则绘制不会发生。)
创建 Graphics Contexts
我们不会通过构造方法来创建 Graphics Contexts,日常中基本上只会在一个场景下创建 Graphics Contexts,那就是绘制生产图片的时候。 我们会使用 UIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat)
函数来创建 Graphics Contexts,该函数不经创建了图片的 Graphics Contexts,还将其设置为了当前的 Graphics Contexts。
绘制一张图片
绘制一张图片所需的基本代码如下:
1 | // 1. 创建图片绘制所需的 *Graphics Contexts* |
图形绘制 API
在 iOS 系统提供的绘图 API 中,有两套是我们经常用到的。一套包含在 UIKit
框架中,另一套包含在 Core Graphics
框架中(也就是 Quartz
或者 Quartz 2D
)。
使用 UIKit
绘图
在使用 UIKit
绘图时,需要 Graphics Contexts 已经是当前的 Contexts
。
对于 UIKit
中的 UIImage
, NSString
, UIBezierPath
和 UIColor
,都只能绘制在当前的 Graphics Contexts,而不能进行额外指定。不过我们可以通过 UIGraphicsPushContext
函数将一个 Graphics Contexts 设置为当前的 Context。需要记住的是,在完成绘图操作后,需要调用 UIGraphicsPopContext
函数将之前的 Graphics Contexts 撤销为当前的 Context。可以使用 Swift 中的 defer
来避免遗忘该步操作。
使用 Core Graphics
绘图
使用 Core Graphics
绘图时,并不需要已经处于一个 Graphics Contexts
中。Core Graphics
绘图 API 是一个全功能的 API,UIKit
的绘制功能其实也是建立在其之上的,Core Graphics
是更低层的 API 接口,基本上都有 C 语言函数实现。
举个 🌰
我们可以在三个地方(UIView
的 drawRect:
, CALayer
相关的 drawInContext:
和 drawLayer:inContext:
,图片绘制),使用两套 API 来绘图,所以一共产生了六种方式。
接下来将使用两种方式来完成同一个任务:画一个有颜色的圆(为了方便区分使用不同的颜色):
1. drawRect:
& UIkit
1 | override func draw(_ rect: CGRect) { |
2. drawRect:
& Core Graphics
1 | override func draw(_ rect: CGRect) { |
3. drawLayer:inContext:
& UIKit
1 | override func draw(_ layer: CALayer, in ctx: CGContext) { |
4. drawLayer:inContext:
& CoreGraphics
1 | override func draw(_ layer: CALayer, in ctx: CGContext) { |
5. 图片绘制 & UIKit
1 | let imageSize = CGSize(width: 200, height: 200) |
6. 图片绘制 & Core Graphics
1 | let imageSize = CGSize(width: 200, height: 200) |