什么是Quartz 2D?
Quartz 2D是Apple的2D绘图引擎,是Core Graphics框架的重要组件。 您可能经常会看到Quartz 2D被称为Core Graphics或简称为CG。
Quartz 2D使用“画家的模型”。 在绘画者的模型中,每个连续的绘画操作将一层“绘画”应用于通常称为页面的输出“画布”。 可以将其想象为从事绘画工作的艺术家。 如果艺术家将整个画布涂成蓝色,然后在画布上涂一些云层,那么这些云层将覆盖它们下面的蓝色。 一旦将某些东西“绘制”到画布上,就无法更改,只能在其上面添加更多的颜料。
Quartz 2D中的所有绘图都是通过CGContextRef
类型的图形上下文完成的。 参照图形上下文,您可以使用Quartz 2D函数绘制上下文,执行操作(例如转换上下文)以及更改图形状态参数(例如线宽和填充颜色)。 Quartz 2D是基于C的API,因此您将调用在上下文中作为参数传递的C函数。
要在iOS上绘制到屏幕上,必须子类化UIView
并重写其drawRect(_:)
方法。 您可以在此drawRect(_:)
方法内进行任何自定义绘图。 您绝对不应在代码中直接调用drawRect(_:)
方法。 如果需要使用新鲜的绘图命令更新屏幕,则应调用setNeedsDisplay()
或setNeedsDisplayInRect(_:)
。
在iOS上使用Quartz 2D时,坐标(0,0)在屏幕的左上方。 当您向右移动时,x坐标会增加,而当您向下移动时,y坐标会增加。
在整个教程中,您可能希望查阅Quartz 2D 编程指南 。 本教程的目的是开始使用Quartz 2D。 有很多内容将不涉及,要完全理解Quartz 2D提供的所有功能,建议您阅读编程指南。
有了这个简短的介绍,让我们开始使用Quartz 2D。
1.准备用于绘制的UIView
假设您已打开一个项目并准备开始使用Quartz 2D,则需要执行的步骤非常简单。 您需要创建一个作为UIView
子类的类,将对象库中的视图添加到Interface Builder中的项目中,并将该视图的类设置为您创建的UIView
子类。 让我们逐步进行此步骤。
步骤1:子类化
UIView
转到文件 > 新建 > 文件...。 在iOS部分下,选择Source ,然后选择Cocoa Touch Class作为模板。
在随后的屏幕上,为您的类命名,确保它是UIView
子类,然后将语言设置为Swift 。 按下一步,然后选择保存新课程的位置。
如果查看新创建的类的源,则将看到drawRect(_:)
方法。 目前已被注释掉,但是稍后我们将对其进行更改。
import UIKit
class DrawLineView: UIView {
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
步骤2:添加视图和设置类
打开项目的故事板,然后打开右侧的对象库 。 在底部的搜索字段中,输入“ UIView ”以过滤掉我们不感兴趣的对象。
将UIView
实例拖到视图控制器上。 选中视图后,打开右侧的Identity Inspector ,然后将Class更改为您命名的子类。
实例化UIView
子类时,将绘制在drawRect(_:)
方法内添加的所有代码。 该视图会自动配置绘图环境,以便您可以立即开始绘图。 该视图配置了在本课程开始时提到的CGContextRef
,并且该视图位于drawRect(_:)
方法内部,您可以对其进行引用。
2.入门项目
为了使我们快速入门,我提供了一个入门项目,该项目已连接了所有视图并可以使用。 UIView
子类以我们将要探索的绘图命令命名。 例如,当我们学习绘图线时,相应的类将被命名为DrawLinesView
。
您可以从GitHub下载入门项目。 我们将在下一步开始编码。
3.获取对图形上下文的引用
在进行任何绘制之前,您需要获取对图形上下文的引用。 这是如下完成的。
let context = UIGraphicsGetCurrentContext()
这将返回一个不透明的类型CGContextRef
,您将把此context
传递到C函数中以进行自定义绘制。 现在我们知道了如何获得对图形上下文的引用,我们可以开始研究绘图命令了。
4.画一条线
如果您已经下载了入门项目,请打开DrawLineView.swift并将以下内容添加到drawRect(_:)
方法中。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor)
CGContextMoveToPoint(context, 0, 0)
CGContextAddLineToPoint(context, 200, 200)
CGContextStrokePath(context)
}
我们首先获得了对绘图上下文的参考,如前所述。 因为这是我们在每个示例中都会做的事情,所以在以后的示例中将不再提及。
CGContextSetStrokeColorWithColor(_:_:)
函数设置线条绘制或描边的颜色。 我们传入的参数是图形上下文和新的笔触颜色。
如果将图形上下文视为画家的画布,则CGContextMoveToPoint(_:_:_:)
函数会将画笔移动到画布上的特定点,从该点开始或继续绘制。 想象在纸上画画,抬起手,然后移动到纸的另一部分并继续画画。 本质上,这就是该方法所完成的。 我们传入图形上下文以及x和y坐标以开始绘制图形。
CGContextAddLineToPoint(_:_:_:)
函数将图形上下文,线段末端的x值和线段末端的y值作为参数。 添加线段后,当前点将设置为线段的端点。 我们从(0,0)开始绘制操作,此绘制操作之后,光标或画笔位于(200,200)。
最后,要进行实际绘制,您需要调用传入图形上下文的CGContextStrokePath(_:)
函数,该函数仅沿我们指定的路径绘制一条线。
生成并运行示例项目以查看效果。
5.绘制矩形
打开DrawRectangleView.swift并将以下内容添加到drawRect(_:)
方法中。 到目前为止,您应该已经熟悉了前两行。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
let rectangle = CGRectMake(50,50,frame.size.width-100,frame.size.height-100)
CGContextAddRect(context,rectangle)
CGContextStrokePath(context)
}
CGRectMake(_:_:_:_:)
函数是CGGeometry的一部分,并提供了一种创建CGRect
结构的简便方法。 顾名思义, CGRect
是一个包含矩形位置和尺寸的结构。 CGRect
具有两个字段origin
和size
,分别是CGPoint
和CGSize
。 如果您不熟悉这些数据类型,请快速阅读CGGeometry参考 。
我们使用CGRectMake(_:_:_:_:)
函数创建一个rectangle
常量,并调用CGContextAddRect(_:_:)
函数,该函数将图形上下文和CGRect
作为参数。 最后,我们调用CGContextStrokePath(context)
绘制矩形。
生成并运行项目,以查看在屏幕上绘制的矩形。
6.画一个圆
打开DrawCircleView.swift并更新如下的drawRect(_:)
方法。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
let rectangle = CGRectMake(50,50,frame.size.width-100,frame.size.height-100)
CGContextAddEllipseInRect(context,rectangle)
CGContextStrokePath(context)
}
您可能想知道为什么在绘制圆时为什么调用CGRectMake(_:_:_:_:)
? 矩形是圆必须适合的区域。 在上面的代码中,我们使用正方形创建一个圆。 如果要绘制椭圆形或椭圆形,则需要使矩形的形状更接近矩形。
然后,我们调用CGContextAddEllipseInRect(_:_:)
函数,该函数将图形上下文和绘制椭圆的矩形作为参数。 通过调用CGContextStrokePath(_:)
绘制圆,并传入图形上下文。
7.画圆弧
打开DrawArcView.swift并将以下代码添加到drawRect(_:)
方法中。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
CGContextAddArc(context, 100,100,50,3.14,0,1)
CGContextStrokePath(context)
}
CGContextAddArc(_:_:_:_:_:_:_:)
函数具有很多参数:
- 图形上下文
- 圆弧中心的x值
- 圆弧中心的y值
- 圆弧的半径
- 与圆弧起点的夹角,以弧度为单位,从x轴正方向开始
- 与圆弧端点的夹角,以弧度为单位,从x轴正方向开始
- 值1可以创建顺时针圆弧,值0可以创建逆时针圆弧
8.画一条路
要绘制更复杂的形状,请创建路径并对其进行描边。 看一下DrawPathView.swift中的drawRect(_:)
方法。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor)
CGContextMoveToPoint(context, 25, 150)
CGContextAddLineToPoint(context, 175, 150)
CGContextAddLineToPoint(context, 100, 50)
CGContextAddLineToPoint(context, 25, 150)
CGContextStrokePath(context)
}
在drawRect(_:)
方法中,我们多次调用CGContextAddLineToPoint(_:_:_:)
以创建三角形。 请注意,三角形不填充,仅被描边。 在下一步中,我们将看到如何用颜色填充三角形。
9.填补道路
打开FillPathView.swift并更新drawRect(_:)
方法,如下所示。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextMoveToPoint(context, 25, 150)
CGContextAddLineToPoint(context, 175, 150)
CGContextAddLineToPoint(context, 100, 50)
CGContextAddLineToPoint(context, 25, 150)
CGContextSetFillColorWithColor(context,UIColor.redColor().CGColor)
CGContextFillPath(context)
}
在上一步中,我们绘制了一条路径,但是您也可以使用特定的颜色填充路径。 在上面的drawRect(_:)
方法中,我们首先为与上一个示例相同的三角形创建路径。 这次我们使用CGContextSetFillColorWithColor(_:_:)
函数设置填充颜色,并调用CGContextFillPath(_:)
而不是CGContextStrokePath(_:)
。
10.填充椭圆
除了填充路径,您还可以填充椭圆和矩形。 在此示例中,我们将填充一个椭圆。 但是,填充矩形非常相似。 文档将告诉您如何完成。 如下所示,更新FillEllipseView.swift中的drawRect(_:)
方法。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, 8.0)
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
let rectangle = CGRectMake(50,50,frame.size.width-100,frame.size.height-100)
CGContextAddEllipseInRect(context, rectangle)
CGContextStrokePath(context)
CGContextSetFillColorWithColor(context,UIColor.greenColor().CGColor)
CGContextFillEllipseInRect(context, rectangle)
}
现在,大多数代码都应该很熟悉。 我们使用一个新函数CGContextSetLineWidth(_:_:)
来设置线宽,我们调用CGContextFillEllipseInRect(_:_:)
来填充椭圆。 该函数将图形上下文和填充椭圆的矩形作为参数。
11.添加线
CGContextAddLines(_:_:_:)
函数是方便的函数,当您要绘制许多相连的直线段时。 在这里,我们使用CGContextAddLines(_:_:_:)
函数从示例的前面重新创建了三角形。 将以下代码添加到AddLinesView.swift中 。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor)
let lines = [CGPointMake(25,150),CGPointMake(175,150),CGPointMake(100,50),CGPointMake(25,150)]
CGContextAddLines(context,lines,4)
CGContextStrokePath(context)
}
CGContextAddLines(_:_:_:)
函数将图形上下文,指定要绘制为CGPoint
结构的线段的起点和终点的值数组以及数组中元素的数量作为参数。 请注意,数组中的第一点指定了起点。
12.绘制渐变
使用Quartz 2D,可以轻松绘制渐变。 支持线性和径向渐变。 在此示例中,我们将绘制线性渐变。 如果您对绘制径向渐变感兴趣,该文档将为您提供帮助。 将以下内容添加到DrawGradientView.swift中 。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let colorspace = CGColorSpaceCreateDeviceRGB()
let colors = [UIColor.redColor().CGColor,UIColor.blueColor().CGColor]
let locations: [CGFloat] = [ 0.0, 0.5]
let gradient = CGGradientCreateWithColors(colorspace,colors,locations)
let startPoint = CGPointMake(0,0)
let endPoint = CGPointMake(200,200)
CGContextDrawLinearGradient(context, gradient,startPoint, endPoint, 0)
}
CGContextDrawLinearGradient(_:_:_:_:_:)
函数采用以下参数:
- 图形上下文
-
CGGradient
结构 - 起点
- 终点
- 选项标志,指定填充是扩展到起点还是终点之外
CGGradient
结构定义整个区域中颜色之间的平滑过渡。 它具有一个颜色空间,两种或多种颜色以及每种颜色的位置。 上例中的常量colorspace
, colors
和locations
表示组成CGGradient
这些部分。
要绘制渐变,我们调用CGContextDrawLinearGradient(_:_:_:_:_:)
函数,在图形上下文中CGGradient
,开始值和结束值以及0,以指示填充范围应超出起始位置。
13.绘制阴影
阴影是绘制在图形对象下方并与图形对象偏移的图像,以使阴影模仿光源投射在图形对象上的效果。 — Quartz 2D编程指南
可以使用两个函数绘制阴影, CGContextSetShadow(_:_:_:)
和CGContextSetShadowWithColor(_:_:_:_:)
。 使用CGContextSetShadow(_:_:_:)
,所有绘制的对象均使用带有1/3 alpha的黑色阴影。 CGContextSetShadowWithColor(_:_:_:_:
函数允许您指定阴影的颜色。
让我们看看这在实践中是如何工作的。 将以下内容添加到SetShadowWithColor.swift中 。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSaveGState(context)
let shadowOffset = CGSizeMake(-15,20)
CGContextSetShadowWithColor(context,shadowOffset,3,UIColor.greenColor().CGColor)
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
let rectangle = CGRectMake(50,50,frame.size.width-100,frame.size.height-100)
CGContextAddRect(context, rectangle)
CGContextStrokePath(context)
CGContextRestoreGState(context)
}
绘制阴影时,应保存图形上下文的状态,进行任何必要的更改,然后恢复图形状态。 我们调用CGContextSaveGState(_:)
保存图形上下文的当前状态,指定阴影的偏移量shadowOffset
,然后调用CGContextSetShadowWithColor(_:_:_:_:)
函数。 此函数以参数为单位:
- 图形上下文
- 阴影的偏移量
- 模糊量
- 阴影的颜色
其余代码对您来说应该是熟悉的。 最后,我们通过调用CGContextRestoreGState(_:)
还原图形上下文,并传入图形上下文。
14.画一张幸福的脸
我认为通过使用我们在本教程中学到的东西来画一张简单的笑脸来结束本教程会很有趣。 将以下内容添加到DrawHappyFaceView.swift中 。
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let face = CGRectMake(50,50,frame.size.width-100,frame.size.height-100)
CGContextAddEllipseInRect(context, face)
CGContextSetFillColorWithColor(context,UIColor.yellowColor().CGColor)
CGContextFillEllipseInRect(context, face)
let leftEye = CGRectMake(75,75,10,10)
CGContextSetFillColorWithColor(context,UIColor.blackColor().CGColor)
CGContextFillEllipseInRect(context, leftEye)
let rightEye = CGRectMake(115,75,10,10)
CGContextFillEllipseInRect(context, rightEye)
CGContextSetLineWidth(context, 3.0)
CGContextAddArc(context, 100,100,30,3.14,0,1)
CGContextStrokePath(context)
}
现在, drawRect(_:)
方法的实现应该很有意义,并且您应该在屏幕上画上一张高兴的脸。
结论
本教程到此结束。 现在,您应该对如何使用Quartz 2D绘图引擎执行自定义绘图有基本的了解。 我希望您通过阅读本教程学到了一些有用的东西。
翻译自: https://code.tutsplus.com/tutorials/an-introduction-to-quartz-2d--cms-24267