Core Graphics — Apple的2D渲染框架,属于Core Foundation的一部分,可在iOS、macOS、tvOS和watchOS上使用。它提供低级别的C-API用于处理矢量和光栅图形,包括绘制轮廓、渐变、阴影、图像和文本。Core Graphics是UIKit、SwiftUI和AppKit的基础。根据Apple Developer Documentation, 2026,该框架用于超过90%的iOS应用程序,因为每个可见元素都通过Core Graphics或其硬件对应物进行渲染。
要点
Core Graphics(也称为Quartz 2D)是实现Apple 2D图形引擎的框架。它基于可移植的Quartz图形库,使用基于页面的绘图模型(page-based),其中每个操作都是序列中的一条命令,类似于在纸上绘图。Core Graphics支持抗锯齿、透明度(alpha合成)和ICC色彩空间。
Core Graphics的架构围绕图形上下文(CGContext)构建,它封装了目标输出设备——屏幕、打印机、PDF文档或光栅缓冲区。上下文管理当前设置:颜色、线宽、变换、裁剪区域和字体。这种方法为不同的输出设备提供统一的API,而无需更改绘图代码。
Core Graphics主要在CPU上运行,使用矢量模型(分辨率无关)。这意味着线条和曲线在渲染时以设备的当前分辨率进行光栅化。与Metal不同,Core Graphics不需要显式管理GPU资源——系统承担所有优化。对于大多数2D任务(图形、图表、自定义UI元素),Core Graphics的性能足以保持60 FPS。
Core Graphics包含大约50种基本类型(不透明类型),每种类型负责特定的功能。所有类型都遵循Core Foundation的内存管理规则:通过名称中包含Create的函数创建,需要调用CGRelease,并且在Objective-C中不与ARC兼容(然而Swift通过CF_RETURNS_RETAINED注释自动管理内存)。
CGContext — 绘图上下文,框架的核心类型。CGPath — 不可变的矢量轮廓。CGColor — 带有色彩空间指定的颜色。CGGradient — 渐变填充。CGImage — 光栅图像。CGLayer — 针对重复绘图优化的离线缓冲区。CGDataProvider — 图像的数据源。CGColorSpace — 色彩空间(sRGB、Display P3、灰色)。
每种类型都设计为最小开销:CGPath可以在不同上下文之间缓存和重用,CGColor在传输时转换为GPU的硬件表示,CGLayer存储预先光栅化的内容。这种架构使Core Graphics既能在简单的UI任务中高效工作,也能在专业图形编辑器中表现出色。
| 类型 | 用途 | 内存管理 |
|---|---|---|
| CGContext | 图形上下文 | CFRelease或在Swift中自动管理 |
| CGPath | 矢量轮廓 | CFRelease(不可变) |
| CGColor | 色彩空间中的颜色 | CFRelease或CGColorCreate |
| CGImage | 光栅图像 | CFRelease(创建后) |
| CGLayer | 离线缓冲区 | CFRelease或CGLayerCreate |
CGPath — 一个不可变对象,描述一系列几何段:直线、贝塞尔曲线、弧线和闭合。与UIBezierPath不同,CGPath不包含绘图方法——它只存储几何信息。要绘图时,将CGPath添加到上下文(CGContextAddPath),然后通过strokePath或fillPath进行绘制。不可变性保证了线程安全性和缓存的可能性。
CGPathCreateMutable创建一个可变路径(CGMutablePathRef)用于逐步构建。在最终组装后,它转换为不可变的CGPathRef。Apple建议最小化CGPath中的点数——过于复杂的轮廓(超过1000个线段)会减慢光栅化速度。为了优化,使用CGPathCreateCopyBySimplifyingPath,它会移除多余的共线点。
要在两个CGPath之间进行形状动画,可以在CAShapeLayer的path属性上使用CABasicAnimation。限制:两个CGPath必须具有相同数量的线段(控制点)。CGPathApply函数允许遍历路径的所有元素并比较它们的结构。如果线段不匹配,动画将不平滑——在这种情况下,使用CADisplayLink手动插值点。
// 创建可变路径
let mutablePath = CGMutablePath()
mutablePath.moveTo(to: CGPoint(x: 0, y: 0))
mutablePath.addLine(to: CGPoint(x: 100, y: 0))
mutablePath.addLine(to: CGPoint(x: 100, y: 100))
mutablePath.closeSubpath()
// 转换为不可变以安全缓存
let immutablePath: CGPath = mutablePath
CATransaction.perform {
shapeLayer.path = immutablePath
}
CGGradient — 定义两种或多种颜色之间沿直线(线性渐变)或从中心到边缘(径向渐变)平滑过渡的对象。CGGradient通过指定色彩空间、CGColorRef数组和从0.0到1.0的停止位置数组来创建。创建后,渐变可以在不同的上下文中多次使用。
CGShading — 更强大的机制,允许定义沿路径的颜色变化函数。与使用固定颜色集的CGGradient不同,CGShading使用回调函数(CGFunctionRef)来计算每个点的颜色。这使得可以创建具有非线性过渡和复合配色方案的复杂渐变。
要在形状内部绘制渐变,需要对该形状的轮廓进行裁剪(clip)。顺序:将路径添加到上下文,调用CGContextClip,然后绘制渐变。没有裁剪,渐变会填充整个上下文。在iOS中,建议使用CGGradient作为更简单和高效的选项——CGShading仅对非标准颜色变化函数是合理的。
func drawGradient(ctx: CGContext, rect: CGRect) {
let colors = [UIColor.red.cgColor, UIColor.blue.cgColor] as CFArray
guard let gradient = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: colors,
locations: [0.0, 1.0]
) else { return }
ctx.saveGState()
ctx.addEllipse(in: rect)
ctx.clip()
ctx.drawLinearGradient(
gradient,
start: CGPoint(x: rect.midX, y: rect.minY),
end: CGPoint(x: rect.midX, y: rect.maxY),
options: []
)
ctx.restoreGState()
}
CGImage — Core Graphics中的光栅图像,可以从文件加载、编程创建或从UIImage获取。CGImage包含像素数据、色彩空间信息、大小、位深度和数据布局。与UIImage不同,CGImage不管理Retina的缩放——这个责任由开发人员通过scale参数承担。
CGLayer — 用于预光栅化静态内容的离线缓冲区。如果同一组图形命令重复绘制(例如背景图案),CGLayer将其光栅化一次,然后简单地复制到上下文中。根据Apple的数据,使用CGLayer可以将重复绘图加速2-5倍(CPU)和高达10倍(硬件加速)。
对于加载图像,使用CGImageSource — 一个强大的API,用于读取元数据、缩略图和来自GIF/APNG的单个帧。CGImageSource允许渐进式加载图像并仅获取必要的片段。在处理大图像(超过屏幕尺寸)时,建议创建具有磁盘缓存选项的CGImageSource,并使用CGImageSourceCreateThumbnailAtIndex显示缩小的版本。
func createLayer(ctx: CGContext, size: CGSize) -> CGLayer? {
guard let layer = CGLayer(ctx, size: size, auxiliaryInfo: nil) else { return nil }
let layerCtx = layer.context !
// 绘制一次
layerCtx.setFillColor(UIColor.lightGray.cgColor)
layerCtx.addEllipse(in: CGRect(origin: .zero, size: size))
layerCtx.fillPath()
return layer
}
// 在draw()中使用
for i in 0..<20 {
ctx.draw(layer, at: CGPoint(x: CGFloat(i) * 30, y: 0))
}
我们将开发一个自定义View,使用Core Graphics在图像上显示水印。水印包括半透明文本和矢量徽标,叠加在原始图像上。代码演示了在单个上下文中处理CGContext、CGImage、文本和透明度。
class WatermarkView: UIView {
private let watermarkText = "IT Sectr"
private let watermarkOpacity: CGFloat = 0.3
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// 绘制背景图像
UIImage(named: "background")?.draw(in: rect)
// 配置水印透明度
ctx.saveGState()
ctx.translateBy(x: rect.midX, y: rect.midY)
ctx.rotate(by: -.pi / 6)
// 半透明文本
let attributes: NSAttributedString.Attributes = [
.font: UIFont.boldSystemFont(ofSize: 48),
.foregroundColor: UIColor.white.withAlphaComponent(watermarkOpacity)
]
let textSize = watermarkText.size(withAttributes: attributes)
watermarkText.draw(
at: CGPoint(x: -textSize.width / 2, y: -textSize.height / 2),
withAttributes: attributes
)
// 文本下方的矢量图标(带叉的圆)
ctx.setStrokeColor(UIColor.white.withAlphaComponent(watermarkOpacity).cgColor)
ctx.setLineWidth(3)
ctx.addEllipse(in: CGRect(x: -25, y: -75, width: 50, height: 50))
ctx.strokePath()
ctx.moveTo(x: -15, y: -50)
ctx.addLineTo(x: 15, y: -50)
ctx.moveTo(x: 0, y: -65)
ctx.addLineTo(x: 0, y: -35)
ctx.strokePath()
ctx.restoreGState()
}
}
代码中使用了Core Graphics的关键功能:saveGState/restoreGState用于将水印效果与背景隔离,translateBy和rotate用于定位和旋转,通过withAlphaComponent实现半透明文本,以及通过addEllipse和addLineTo实现矢量图标。整个水印在背景图像之后作为一个整体绘制。
在生产环境中为照片创建水印时,考虑性能很重要。如果图像大于屏幕,在应用水印之前通过CGImageSourceCreateThumbnailAtIndex缩小它。Core Graphics在CPU上处理所有操作——对于批量处理数十张图像,使用后台队列和CGBitmapContext以避免阻塞UI。
常见问题
UIKit — 高级UI框架,内部使用Core Graphics进行渲染。Core Graphics — 用于2D图形的低级C-API。UIKit对于标准元素更方便,Core Graphics提供对每个像素的完全控制。
Core Graphics适用于静态2D图形、图表和自定义UI元素。Metal — 用于3D图形、复杂动画和通过GPU加速处理大型像素数组。对于95%的UI任务,Core Graphics已经足够。
使用CGImageSource通过createThumbnailAtIndex加载缩略图而不是完整加载。要显示大图像,创建带有downsample选项的CGImage。当不再需要时,通过CGImageRelease释放CGImage。
是的,Core Graphics通过CGPDFDocument用于读取和CGPDFContext用于创建PDF文档,提供完整的PDF支持。可以将任何CGContext内容直接绘制到PDF页面并保留矢量。
Core Graphics在屏幕上渲染时自动使用设备的分辨率。对于离线上下文,使用参数scale = 0(系统缩放比例)的UIGraphicsBeginImageContextWithOptions,或为Retina/Retina HD明确指定2.0/3.0。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。