CoreGraphic框架解析 (十)—— 一个简单小游戏 (二
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.02.01 星期五 |
前言
quartz
是一个通用的术语,用于描述在iOS
和MAC OS X
中整个媒体层用到的多种技术 包括图形、动画、音频、适配。Quart 2D
是一组二维绘图和渲染API
,Core Graphic
会使用到这组API
,Quartz Core
专指Core Animation
用到的动画相关的库、API
和类。CoreGraphics
是UIKit
下的主要绘图系统,频繁的用于绘制自定义视图。Core Graphics
是高度集成于UIView
和其他UIKit
部分的。Core Graphics
数据结构和函数可以通过前缀CG
来识别。在app中很多时候绘图等操作我们要利用CoreGraphic
框架,它能绘制字符串、图形、渐变色等等,是一个很强大的工具。感兴趣的可以看我另外几篇。
1. CoreGraphic框架解析(一)—— 基本概览
2. CoreGraphic框架解析(二)—— 基本使用
3. CoreGraphic框架解析(三)—— 类波浪线的实现
4. CoreGraphic框架解析(四)—— 基本架构补充
5. CoreGraphic框架解析 (五)—— 基于CoreGraphic的一个简单绘制示例 (一)
6. CoreGraphic框架解析 (六)—— 基于CoreGraphic的一个简单绘制示例 (二)
7. CoreGraphic框架解析 (七)—— 基于CoreGraphic的一个简单绘制示例 (三)
8. CoreGraphic框架解析 (八)—— 基于CoreGraphic的一个简单绘制示例 (四)
9. CoreGraphic框架解析 (九)—— 一个简单小游戏 (一)
Masking Patterns
掩模图案(Masking patterns)
在图案单元格绘制方法之外定义其颜色信息。 这允许您更改图案颜色以满足您的需要。
这是一个没有颜色关联的遮罩图案的例子:
有了模式,您现在可以应用颜色。 下面的第一个示例显示应用于蒙版的蓝色,第二个示例显示橙色:
现在,您将把您使用过的模式更改为蒙版模式(masking pattern)
。
从drawPattern
替换以下代码:
context.setFillColor(UIColor.yellow.cgColor)
context.setStrokeColor(UIColor.darkGray.cgColor)
context.drawPath(using: .fillStroke)
使用下面的代码
context.fillPath()
这会将代码恢复为填充路径。
用以下内容替换pattern
分配:
let pattern = CGPattern(
info: nil,
bounds: CGRect(x: 0, y: 0, width: 20, height: 20),
matrix: transform,
xStep: 25,
yStep: 25,
tiling: .constantSpacing,
isColored: false,
callbacks: &callbacks)
这会将isColored
参数设置为false
,从而将模式更改为屏蔽模式。 您还将垂直和水平间距增加到25。现在,您需要为模式提供颜色空间信息。
用以下内容替换patternSpace
赋值:
let baseSpace = CGColorSpaceCreateDeviceRGB()
let patternSpace = CGColorSpace(patternBaseSpace: baseSpace)!
在这里,您将获得对标准设备相关RGB颜色空间的引用。 然后,您将图案颜色空间更改为此值,而不是之前的nil
值。
在下面,替换这些行:
var alpha : CGFloat = 1.0
context.setFillPattern(pattern!, colorComponents: &alpha)
用下面的代码
let fillColor: [CGFloat] = [0.0, 1.0, 1.0, 1.0]
context.setFillPattern(pattern!, colorComponents: fillColor)
填充图案时,这会在蒙版下方创建一种颜色。
运行playground
。 您模式的颜色更新以反映在draw
方法之外配置的cyan
颜色设置:
是时候看看如何描边和填充掩模图案了。 这就像描边彩色图案。
使用以下内容替换drawPattern
定义中的context.fillPath()
行:
context.setStrokeColor(UIColor.darkGray.cgColor)
context.drawPath(using: .fillStroke)
虽然您在draw(_:)
中设置了stroke颜色,但您的图案颜色仍然在方法之外设置。
运行playground
以查看stroked
模式:
您现在已经积累了不同模式配置和masking patterns
的经验。 您可以开始构建Recall
所需的模式。
Creating the Game Pattern
在视图控制器在playground
中显示之前添加以下代码:
extension UIBezierPath {
// 1
convenience init(triangleIn rect: CGRect) {
self.init()
// 2
let topOfTriangle = CGPoint(x: rect.width / 2, y: 0)
let bottomLeftOfTriangle = CGPoint(x: 0, y: rect.height)
let bottomRightOfTriangle = CGPoint(x: rect.width, y: rect.height)
// 3
self.move(to: topOfTriangle)
self.addLine(to: bottomLeftOfTriangle)
self.addLine(to: bottomRightOfTriangle)
// 4
self.close()
}
}
下面进行详细说明:
- 1) 扩展
UIBezierPath
以创建三角形路径。 - 2) 指定构成三角形的三个点。
- 3) 从顶部开始绘制三角形。
move(to :)
开始路径,addLine(to :)
将line
添加到路径中。 - 4) 关闭路径。
将以下结构添加到PatternView
的顶部:
public struct Constants {
static let patternSize: CGFloat = 30.0
static let patternRepeatCount = 2
}
这些代表您在设置模式时将使用的常量。 patternSize
定义模式单元格大小。 patternRepeatCount
定义模式视图中的模式单元格数。
在Constants
定义后添加以下内容:
let drawTriangle: CGPatternDrawPatternCallback = { _, context in
let trianglePath = UIBezierPath(triangleIn:
CGRect(x: 0, y: 0,
width: Constants.patternSize,
height: Constants.patternSize))
context.addPath(trianglePath.cgPath)
context.fillPath()
}
这定义了一个用于绘制三角形图案的新函数。 调用UIBezierPath(triangleIn :)
返回表示三角形的路径。 然后,在绘制之前将此路径添加到上下文中。
请注意,该函数未指定填充颜色,因此它可以是masking pattern
。
在draw(_ :)
中,将callbacks
更改为以下内容:
var callbacks = CGPatternCallbacks(
version: 0, drawPattern: drawTriangle, releaseInfo: nil)
您现在正在使用三角绘制函数。
删除drawPattern
,因为它不再需要。 人们只能在圈子里走了这么长时间。
同样在draw(_ :)
中,用以下代码替换分配transform
和pattern
的代码:
// 1
let patternStepX: CGFloat =
rect.width / CGFloat(Constants.patternRepeatCount)
let patternStepY: CGFloat =
rect.height / CGFloat(Constants.patternRepeatCount)
// 2
let patternOffsetX: CGFloat = (patternStepX - Constants.patternSize) / 2.0
let patternOffsetY: CGFloat = (patternStepY - Constants.patternSize) / 2.0
// 3
let transform = CGAffineTransform(translationX: patternOffsetX, y: patternOffsetY)
// 4
let pattern = CGPattern(
info: nil,
bounds: CGRect(
x: 0,
y: 0,
width: Constants.patternSize,
height: Constants.patternSize),
matrix: transform,
xStep: patternStepX,
yStep: patternStepY,
tiling: .constantSpacing,
isColored: false,
callbacks: &callbacks)
这是代码的作用,一步一步:
- 1) 使用视图的宽度和高度以及视图中的图案单元格数计算水平和垂直步长。
- 2) 计算出尺寸,使图案单元在其边界内水平和垂直居中。
- 3) 根据您定义的居中变量设置
CGAffineTransform
转换。 - 4) 根据计算出的参数创建模式对象。
运行playground
,您应该在每个方向上看到两个三角形,在其边界内垂直和水平居中:
现在,您将获得更接近Recall
应用程序的背景颜色。
在MyViewController
中,更改loadView()
中的背景颜色设置,如下所示:
view.backgroundColor = .lightGray
接下来转到PatternView
并在draw(_ :)
中更改上下文填充设置,如下所示:
UIColor.white.setFill()
运行playground
,您的主视图背景现在应该是灰色的,图案视图为白色背景:
1. Customizing the Pattern View
现在您已正确显示基本模式,您可以进行更改以控制模式方向。
在Constants
定义之后,在PatternView
顶部附近添加以下枚举:
enum PatternDirection: CaseIterable {
case left
case top
case right
case bottom
}
这表示三角形可以指向的不同方向。
在PatternDirection
定义之后添加以下类属性:
var fillColor: [CGFloat] = [1.0, 0.0, 0.0, 1.0]
var direction: PatternDirection = .top
这表示您将应用于masking pattern
和图案pattern
方向的颜色。 该类设置默认颜色为红色,默认方向为top
。
删除在draw(_ :)
底部附近的本地fillColor
声明。 这将确保您使用class
属性。
用以下内容替换transform
赋值:
// 1
var transform: CGAffineTransform
// 2
switch direction {
case .top:
transform = .identity
case .right:
transform = CGAffineTransform(rotationAngle: CGFloat(0.5 * .pi))
case .bottom:
transform = CGAffineTransform(rotationAngle: CGFloat(1.0 * .pi))
case .left:
transform = CGAffineTransform(rotationAngle: CGFloat(1.5 * .pi))
}
// 3
transform = transform.translatedBy(x: patternOffsetX, y: patternOffsetY)
这就是上面代码的作用:
- 1) 为模式转换声明一个
CGAffineTransform
变量。 - 2) 如果模式方向为
top
,则将变换分配给单位矩阵。 否则,变换是基于方向的旋转。 例如,如果图案指向右侧,则旋转为π/ 2
弧度或顺时针90º。 - 3) 应用
CGAffineTransform
转换以使模式单元在其边界内居中。
运行playground
,根据您的默认图案填充颜色,您的三角形为红色:
现在是设置代码来控制和测试图案颜色和方向的好时机。
在类属性定义之后在PatternView
中添加以下方法:
init(fillColor: [CGFloat], direction: PatternDirection = .top) {
self.fillColor = fillColor
self.direction = direction
super.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
这将设置一个初始化器,它接收填充颜色和图案方向。 direction
参数具有默认值。
您还在sb初始化视图时添加了所需的初始化程序。 将代码传输到应用程序后,您将需要此功能。
在MyViewController
中,更改了patternView
赋值,因为您已经更改了初始化程序:
let patternView = PatternView(
fillColor: [0.0, 1.0, 0.0, 1.0],
direction: .right)
在这里,您使用非默认值实例化模式视图。
运行playground
,你的三角形现在是绿色的并指向右边:
恭喜! 您已经使用Playgrounds
对您的模式进行了原型设计。 是时候在Recall
中使用这种模式了。
2. Updating the App Pattern View
打开Recall
启动项目。 转到PatternView.swift
并将UIBezierPath
扩展从playground
复制到文件末尾。
接下来,将PatternView.swift
中的PatternView
类替换为playground
中的类。
注意:通过使用
Xcode
的代码折叠功能,您可以大大简化此过程。 在playground
中,将光标放在class PatternView: UIView {
的左大括号后面并从菜单中选择Editor ▸ Code Folding ▸ Fold
。 双击生成的折叠线以选择整个类,然后按Command-C
。 在项目中,重复该过程以折叠并选择该类。 按Command-V
替换它。
构建并运行应用程序。 你应该看到这样的东西:
有点不对劲。 您的图案似乎卡在默认模式下。 看起来新游戏视图没有刷新模式视图。
转到GameViewController.swift
并将以下内容添加到setupPatternView(_:towards:havingColor:)
的末尾:
patternView.setNeedsDisplay()
这会提示系统重绘图案,以便它获取新的图案信息。
构建并运行应用程序。 您现在应该看到混合的颜色和方向:
点击其中一个答案按钮并玩游戏以检查一切是否按预期工作。
恭喜您完成Recall
!
Performance
Core Graphics
模式非常快。 以下是可用于绘制模式的几个选项:
- 1) 使用您在本教程中学习的
Core Graphics
模式API。 - 2) 使用UIKit包装方法,如
UIColor(patternImage :)
。 - 3) 在具有许多
Core Graphics
调用的循环中绘制所需的模式。
如果您的模式只绘制一次,则UIKit
包装方法最简单。 它的性能也应该与较低级别的Core Graphics
调用相当。 一个例子是背景图案。
Core Graphics
可以在后台线程中工作,而不像UIKit
,它在主线程上运行。 Core Graphics
模式在复杂的绘图或动态模式下性能更好。
在循环中绘制图案将是最慢的。 Core Graphics
模式使绘制调用一次并缓存结果,使其更有效。
后记
本篇主要讲述了一个简单小游戏,感兴趣的给个赞或者关注~~~