CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)

版本记录

版本号 时间
V1.0 2019.02.01 星期五

前言

quartz是一个通用的术语,用于描述在iOSMAC OS X 中整个媒体层用到的多种技术 包括图形、动画、音频、适配。Quart 2D 是一组二维绘图和渲染APICore Graphic会使用到这组APIQuartz Core专指Core Animation用到的动画相关的库、API和类。CoreGraphicsUIKit下的主要绘图系统,频繁的用于绘制自定义视图。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颜色设置:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第1张图片

是时候看看如何描边和填充掩模图案了。 这就像描边彩色图案。

使用以下内容替换drawPattern定义中的context.fillPath()行:

context.setStrokeColor(UIColor.darkGray.cgColor)
context.drawPath(using: .fillStroke)

虽然您在draw(_:)中设置了stroke颜色,但您的图案颜色仍然在方法之外设置。

运行playground以查看stroked模式:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第2张图片

您现在已经积累了不同模式配置和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(_ :)中,用以下代码替换分配transformpattern的代码:

// 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,您应该在每个方向上看到两个三角形,在其边界内垂直和水平居中:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第3张图片

现在,您将获得更接近Recall应用程序的背景颜色。

MyViewController中,更改loadView()中的背景颜色设置,如下所示:

view.backgroundColor = .lightGray

接下来转到PatternView并在draw(_ :)中更改上下文填充设置,如下所示:

UIColor.white.setFill()

运行playground,您的主视图背景现在应该是灰色的,图案视图为白色背景:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第4张图片

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,根据您的默认图案填充颜色,您的三角形为红色:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第5张图片

现在是设置代码来控制和测试图案颜色和方向的好时机。

在类属性定义之后在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,你的三角形现在是绿色的并指向右边:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第6张图片

恭喜! 您已经使用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替换它。

构建并运行应用程序。 你应该看到这样的东西:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第7张图片

有点不对劲。 您的图案似乎卡在默认模式下。 看起来新游戏视图没有刷新模式视图。

转到GameViewController.swift并将以下内容添加到setupPatternView(_:towards:havingColor:)的末尾:

patternView.setNeedsDisplay()

这会提示系统重绘图案,以便它获取新的图案信息。

构建并运行应用程序。 您现在应该看到混合的颜色和方向:

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第8张图片

点击其中一个答案按钮并玩游戏以检查一切是否按预期工作。

恭喜您完成Recall

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第9张图片

Performance

Core Graphics模式非常快。 以下是可用于绘制模式的几个选项:

  • 1) 使用您在本教程中学习的Core Graphics模式API。
  • 2) 使用UIKit包装方法,如UIColor(patternImage :)
  • 3) 在具有许多Core Graphics调用的循环中绘制所需的模式。

如果您的模式只绘制一次,则UIKit包装方法最简单。 它的性能也应该与较低级别的Core Graphics调用相当。 一个例子是背景图案。

Core Graphics可以在后台线程中工作,而不像UIKit,它在主线程上运行。 Core Graphics模式在复杂的绘图或动态模式下性能更好。

在循环中绘制图案将是最慢的。 Core Graphics模式使绘制调用一次并缓存结果,使其更有效。

后记

本篇主要讲述了一个简单小游戏,感兴趣的给个赞或者关注~~~

CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)_第10张图片

你可能感兴趣的:(CoreGraphic框架解析 (十)—— 一个简单小游戏 (二))