CoreGraphic框架解析 (二十六) —— 以高效的方式繪制圖案(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2021.05.30 星期日

前言

quartz是一個(gè)通用的術(shù)語(yǔ)芳室,用于描述在iOSMAC OS X 中整個(gè)媒體層用到的多種技術(shù) 包括圖形、動(dòng)畫呈枉、音頻趁尼、適配埃碱。Quart 2D 是一組二維繪圖和渲染APICore Graphic會(huì)使用到這組API酥泞,Quartz Core專指Core Animation用到的動(dòng)畫相關(guān)的庫(kù)砚殿、API和類。CoreGraphicsUIKit下的主要繪圖系統(tǒng)芝囤,頻繁的用于繪制自定義視圖似炎。Core Graphics是高度集成于UIView和其他UIKit部分的。Core Graphics數(shù)據(jù)結(jié)構(gòu)和函數(shù)可以通過前綴CG來(lái)識(shí)別悯姊。在app中很多時(shí)候繪圖等操作我們要利用CoreGraphic框架羡藐,它能繪制字符串、圖形悯许、漸變色等等仆嗦,是一個(gè)很強(qiáng)大的工具。感興趣的可以看我另外幾篇先壕。
1. CoreGraphic框架解析(一)—— 基本概覽
2. CoreGraphic框架解析(二)—— 基本使用
3. CoreGraphic框架解析(三)—— 類波浪線的實(shí)現(xiàn)
4. CoreGraphic框架解析(四)—— 基本架構(gòu)補(bǔ)充
5. CoreGraphic框架解析 (五)—— 基于CoreGraphic的一個(gè)簡(jiǎn)單繪制示例 (一)
6. CoreGraphic框架解析 (六)—— 基于CoreGraphic的一個(gè)簡(jiǎn)單繪制示例 (二)
7. CoreGraphic框架解析 (七)—— 基于CoreGraphic的一個(gè)簡(jiǎn)單繪制示例 (三)
8. CoreGraphic框架解析 (八)—— 基于CoreGraphic的一個(gè)簡(jiǎn)單繪制示例 (四)
9. CoreGraphic框架解析 (九)—— 一個(gè)簡(jiǎn)單小游戲 (一)
10. CoreGraphic框架解析 (十)—— 一個(gè)簡(jiǎn)單小游戲 (二)
11. CoreGraphic框架解析 (十一)—— 一個(gè)簡(jiǎn)單小游戲 (三)
12. CoreGraphic框架解析 (十二)—— Shadows 和 Gloss (一)
13. CoreGraphic框架解析 (十三)—— Shadows 和 Gloss (二)
14. CoreGraphic框架解析 (十四)—— Arcs 和 Paths (一)
15. CoreGraphic框架解析 (十五)—— Arcs 和 Paths (二)
16. CoreGraphic框架解析 (十六)—— Lines, Rectangles 和 Gradients (一)
17. CoreGraphic框架解析 (十七)—— Lines, Rectangles 和 Gradients (二)
18. CoreGraphic框架解析 (十八) —— 如何制作Glossy效果的按鈕(一)
19. CoreGraphic框架解析 (十九) —— 如何制作Glossy效果的按鈕(二)
20. CoreGraphic框架解析 (二十) —— Curves and Layers(一)
21. CoreGraphic框架解析 (二十一) —— Curves and Layers(二)
22. CoreGraphic框架解析 (二十二) —— Gradients 和 Contexts的簡(jiǎn)單示例(一)
23. CoreGraphic框架解析 (二十三) —— Gradients 和 Contexts的簡(jiǎn)單示例(二)
24. CoreGraphic框架解析 (二十四) —— 基于Core Graphic的重復(fù)圖案的繪制(一)
25. CoreGraphic框架解析 (二十五) —— 基于Core Graphic的重復(fù)圖案的繪制(二)

開始

首先看下主要內(nèi)容:

了解如何使用Core Graphics以高效的方式繪制圖案欧啤。內(nèi)容來(lái)自翻譯

接著看下寫作環(huán)境:

Swift 5, iOS 14, Xcode 12

下面就是正文啦启上。

Core Graphics是一組強(qiáng)大而友好的 API邢隧,用于在 UIKit應(yīng)用程序中進(jìn)行繪圖。 除了形狀和漸變等基元之外冈在,您還可以使用 Core Graphics編寫圖案倒慧。 Core Graphics Patterns是一組任意的圖形操作集,可以平鋪以填充一個(gè)區(qū)域包券。 您可以使用重復(fù)的形狀為您的應(yīng)用程序創(chuàng)建華麗的背景纫谅。 Core Graphics Patterns是一種縮放繪圖以填充屏幕上任何形狀的高效方法。

在本教程中溅固,您將學(xué)習(xí)如何使用 Core Graphics 執(zhí)行以下操作:

  • 創(chuàng)建基于路徑的繪圖付秕。
  • 畫一個(gè)圖案(pattern)
  • 變換圖案侍郭。
  • 使用模式完成一個(gè)名為 Recall 的模式識(shí)別游戲询吴。

注意:如果您是 Core Graphics 的新手,最好查看我們關(guān)于該主題的一些入門級(jí)教程亮元。 考慮學(xué)習(xí) Lines, Rectangles, and Gradients以及Arcs and Paths教程猛计,以更好地理解您將在此處構(gòu)建的基礎(chǔ)。

打開入門應(yīng)用程序爆捞。 您會(huì)看到以下內(nèi)容(顏色和字母可能會(huì)有所不同):

RecallLeft vs Right 大腦訓(xùn)練應(yīng)用程序中的游戲中獲得靈感奉瘤。 游戲的目標(biāo)是為視野中的物體選擇最受歡迎的方向。 一旦您做出選擇,就會(huì)顯示一組新的對(duì)象蚁飒。 在游戲結(jié)束前您有五次嘗試機(jī)會(huì)痛垛。

Recall 在四個(gè)象限中對(duì)模式對(duì)象進(jìn)行分組燃乍。 入門應(yīng)用程序中的每個(gè)象限都有一個(gè)標(biāo)簽纱耻。 文本代表方向知给,背景顏色代表填充顏色坛芽。

作為一個(gè)起點(diǎn)崩掘,游戲相當(dāng)平庸吼驶。

你的任務(wù)是使用 Core Graphics 模式將這個(gè)悲傷的應(yīng)用程序變成下面的完成的應(yīng)用程序:

Xcode 中查看項(xiàng)目惩激。這些是主要文件:

  • GameViewController.swift:控制游戲玩法并顯示游戲視圖。
  • ResultViewController.swift:顯示最終得分和重新開始游戲的按鈕蟹演。
  • PatternView.swift:顯示游戲視圖中象限之一的模式視圖风钻。

在本教程即將結(jié)束時(shí),您將增強(qiáng) PatternView 以顯示所需的圖案酒请。

首先骡技,您將在 Playground 中對(duì)新的和改進(jìn)的 PatternView 進(jìn)行原型設(shè)計(jì)。這使您可以在學(xué)習(xí) Core Graphics 模式的來(lái)龍去脈的同時(shí)更快地進(jìn)行迭代羞反。完成后布朦,您將相關(guān)代碼轉(zhuǎn)移到 Recall啟動(dòng)項(xiàng)目。

Xcode 中昼窗,轉(zhuǎn)到File ? New ? Playground….是趴。選擇Single View模板。單擊 Next澄惊,將 Playground 命名為 PatternView.playground唆途,將其添加到 Recall 工作區(qū)和 Recall 文件夾組,然后單擊 Create掸驱。您的 Playground 包含一個(gè)帶有單個(gè)視圖的視圖控制器肛搬。

注意:如果 Xcode 在嘗試加載新程序時(shí)出現(xiàn)錯(cuò)誤,那么古老的“重新啟動(dòng) Xcode”應(yīng)該會(huì)讓您繼續(xù)前進(jìn)毕贼。

選擇 Editor ? Run Playground來(lái)執(zhí)行 Playground温赔。單擊Show the Assistant Editor以顯示您的入門視圖。它顯示標(biāo)志性的“Hello World鬼癣!”結(jié)果:

在您閱讀下一部分時(shí)陶贼,您將用您的模式視圖替換初始視圖。 是時(shí)候開始了扣溺!


Understanding the Anatomy of a Pattern

在我們之前的previous Core Graphics tutorials教程中骇窍,您已經(jīng)看到了如何定義和繪制路徑:

路徑是一組描述形狀的指令。 上面的示例顯示您用黑線描邊路徑锥余。 右邊的路徑用黑色描邊并用橙色填充。

使用 Core Graphics痢掠,您還可以使用圖案(pattern)描邊或填充路徑驱犹。 下面的示例顯示了填充路徑的彩色圖案:

1. Setting up the Pattern

您可以通過執(zhí)行以下操作來(lái)設(shè)置模式:

  • 1) 編寫一個(gè)繪制單個(gè)圖案單元格的方法嘲恍。
  • 2) 使用包括如何繪制和放置單個(gè)單元格的參數(shù)創(chuàng)建圖案。
  • 3) 定義您的圖案將使用的顏色信息雄驹。
  • 4) 使用您創(chuàng)建的圖案繪制所需的路徑佃牛。

現(xiàn)在,查看帶有額外填充的略有不同的圖案單元格医舆。 黑色細(xì)邊框顯示單元格的邊界:

您將編寫一個(gè)在單元格cell邊界內(nèi)進(jìn)行繪制的 draw方法俘侠。 Core Graphics 剪裁在單元格邊界之外繪制的任何內(nèi)容。 Core Graphics 還希望您每次都以完全相同的方式繪制圖案單元格蔬将。

在設(shè)置圖案單元格時(shí)爷速,您的繪制方法可以應(yīng)用顏色。 這是一個(gè)彩色圖案霞怀。 未著色或遮罩圖案是在draw方法之外應(yīng)用填充顏色的圖案惫东。 這使您可以靈活地在有意義的地方設(shè)置圖案顏色。

Core Graphics 反復(fù)調(diào)用你的 draw 方法來(lái)設(shè)置你的圖案毙石。 圖案創(chuàng)建參數(shù)定義了圖案的外觀廉沮。 下面的示例顯示了一個(gè)基本的重復(fù)圖案,其中單元格彼此相鄰排列:

您可以在配置圖案時(shí)指定圖案單元格之間的間距:

您還可以應(yīng)用變換來(lái)更改圖案的外觀徐矩。 下圖顯示了在由模糊邊框表示的空間內(nèi)繪制的圖案:

第一個(gè)顯示未更改的圖案滞时。 在第二個(gè)中,您會(huì)看到一個(gè)translated的模式滤灯。 第三個(gè)顯示旋轉(zhuǎn)的圖案坪稽。 同樣,圖案單元格周圍的黑色邊框突出顯示其邊界力喷。

配置模式時(shí)刽漂,您有很多可用的選項(xiàng)。 您將在下一部分開始將所有這些放在一起弟孟。

2. Creating the Pattern View

PatternView.playground 中的視圖控制器類之前添加以下代碼:

class PatternView: UIView {
  override func draw(_ rect: CGRect) {
    // 1
    guard let context = UIGraphicsGetCurrentContext()
    else { return }
    // 2
    UIColor.orange.setFill()
    // 3
    context.fill(rect)
  }
}

這代表您的圖案的自定義視圖贝咙。 在這里,您重寫 draw(_:)以執(zhí)行以下操作:

  • 1) 獲取視圖的圖形上下文拂募。
  • 2) 設(shè)置上下文的當(dāng)前填充顏色庭猩。
  • 3) 使用當(dāng)前的填充顏色填充整個(gè)上下文。

將圖形上下文視為您可以在其上繪制的畫布陈症。 上下文包含諸如將填充或描邊路徑的顏色等信息蔼水。 在使用上下文的顏色信息繪制路徑之前,您可以在畫布中繪制路徑录肯。

MyViewController 中趴腋,將 loadView() 中與 label相關(guān)的代碼替換為以下代碼:

let patternView = PatternView()
patternView.frame = CGRect(x: 10, y: 10, width: 200, height: 200)
view.addSubview(patternView)

這將創(chuàng)建圖案視圖的實(shí)例,設(shè)置frame并將其添加到視圖view中。

Shift-Command-Return 運(yùn)行 Playground优炬。 之前的label不見了颁井,取而代之的是一個(gè)橙色的子視圖:

著色只是旅程的開始。 你知道還有更多的來(lái)源蠢护!

3. Drawing a Black Circle in the Pattern Cell

PatternView的頂部?jī)?nèi)添加以下屬性:

let drawPattern: CGPatternDrawPatternCallback = { _, context in
  context.addArc(
    center: CGPoint(x: 20, y: 20),
    radius: 10.0,
    startAngle: 0,
    endAngle: 2.0 * .pi,
    clockwise: false)
  context.setFillColor(UIColor.black.cgColor)
  context.fillPath()
}

上面的代碼在圖形上下文中繪制了一個(gè)圓形路徑并用黑色填充它雅宾。

這表示您圖案單元格的繪制方法,它是 CGPatternDrawPatternCallback 類型葵硕。 這是一個(gè)帶有兩個(gè)參數(shù)的閉包:

  • 1) 指向與圖案關(guān)聯(lián)的私有數(shù)據(jù)的指針眉抬。 你沒有使用私有數(shù)據(jù),所以你在這里使用了一個(gè)未命名的參數(shù)懈凹。
  • 2) 用于繪制圖案單元格的圖形context蜀变。

將以下代碼添加到 draw(_:)的末尾:

var callbacks = CGPatternCallbacks(
  version: 0, 
  drawPattern: drawPattern, 
  releaseInfo: nil)

CGPatternCallbacks 是一種用于保存用于繪制圖案的回調(diào)的結(jié)構(gòu)。 有兩種類型的回調(diào) - 一種用于圖案蘸劈,您已經(jīng)創(chuàng)建昏苏,另一種用于清理和釋放您沒有使用的任何私有數(shù)據(jù)。 如果您在圖案中使用私有數(shù)據(jù)威沫,您通常會(huì)設(shè)置一個(gè)release回調(diào)贤惯。 由于您沒有在 draw 方法中使用私有數(shù)據(jù),因此您為此回調(diào)傳遞 nil棒掠。

4. Creating the Pattern

要?jiǎng)?chuàng)建圖案孵构,請(qǐng)?jiān)谏厦娴拇a之后添加以下內(nèi)容:

guard let pattern = CGPattern(
  info: nil,
  bounds: CGRect(x: 0, y: 0, width: 20, height: 20),
  matrix: .identity,
  xStep: 50,
  yStep: 50,
  tiling: .constantSpacing,
  isColored: true,
  callbacks: &callbacks)
else { return }

這將創(chuàng)建一個(gè)圖案(pattern)對(duì)象。 在上面的代碼中烟很,您傳入以下參數(shù):

  • info:指向您要在圖案回調(diào)中使用的任何私有數(shù)據(jù)的指針颈墅。 你在這里傳入 nil 因?yàn)槟銢]有使用任何。
  • bounds:圖案單元格的邊界框雾袱。
  • matrix:表示要應(yīng)用的變換的矩陣恤筛。 您傳入單位矩陣,因?yàn)槟鷽]有應(yīng)用任何變換芹橡。
  • xStep:圖案單元格之間的水平間距毒坛。
  • yStep:圖案單元格之間的垂直間距。
  • tilingCore Graphics 應(yīng)該使用的技術(shù)來(lái)解決用戶空間單位和設(shè)備像素之間的差異林说。
  • isColored:圖案單元格繪制方法是否應(yīng)用顏色煎殷。 您將其設(shè)置為 true,因?yàn)槟?draw 方法設(shè)置了一種顏色腿箩。
  • callbacks:指向保存圖案回調(diào)的結(jié)構(gòu)的指針豪直。

在圖案分配后立即添加以下代碼:

var alpha: CGFloat = 1.0
context.setFillPattern(pattern, colorComponents: &alpha)
context.fill(rect)

上面的代碼為圖形上下文設(shè)置填充圖案。 對(duì)于彩色圖案珠移,還必須傳遞一個(gè)alpha值以指定圖案的不透明度弓乙。 圖案繪制方法提供顏色末融。 最后,代碼用圖案繪制視圖的frame區(qū)域唆貌。

Shift-Command-Return 運(yùn)行 Playground滑潘。 你的圖案沒有出現(xiàn)垢乙。 奇怪的锨咙。 這是怎么回事?

5. Setting the Pattern’s Color Space

您需要向 Core Graphics 提供有關(guān)圖案顏色空間的信息追逮,以便它知道如何處理圖案顏色酪刀。 色彩空間指定如何解釋要顯示的色彩值。

alpha 聲明之前添加以下內(nèi)容:

// 1
guard let patternSpace = CGColorSpace(patternBaseSpace: nil)
else { return }
// 2
context.setFillColorSpace(patternSpace)

下面是代碼的作用:

  • 1) 創(chuàng)建圖案色彩空間钮孵。 對(duì)于彩色圖案骂倘,基本空間參數(shù)應(yīng)該為nil。 這將著色委托給您的圖案單元格繪制方法巴席。
  • 2) 將填充顏色空間設(shè)置為您定義的圖案顏色空間历涝。

playground。 是的漾唉! 現(xiàn)在荧库,您應(yīng)該看到一個(gè)圓形的黑色圖案:

接下來(lái),您將了解有關(guān)配置圖案的更多信息赵刑。

6. Configuring the Pattern

在您的 Playground 中分衫,更改設(shè)置pattern的間距參數(shù)如下:

xStep: 30,
yStep: 30,

playground。 請(qǐng)注意般此,圓點(diǎn)似乎彼此更接近:

這是有道理的蚪战,因?yàn)槟s小了圖案單元之間的步長(zhǎng)。

現(xiàn)在铐懊,更改間距參數(shù)如下:

xStep: 20,
yStep: 20,

運(yùn)行playground邀桑。 你的圈子變成了四分之一:

7. Changing the Centers of the Circles

要了解原因,請(qǐng)注意您的 draw 方法返回一個(gè)以 (20, 20)為中心科乎、半徑為 10 的圓壁畸。 圖案的水平和垂直位移為 20。單元格的邊界框在原點(diǎn)(0,0)處為 20×20喜喂。 這導(dǎo)致從右下邊緣開始的重復(fù)四分之一圓瓤摧。

將繪制圓的 (context.addArc)drawPattern更改為以下內(nèi)容:

context.addArc(
  center: CGPoint(x: 10, y: 10), 
  radius: 10.0,
  startAngle: 0, 
  endAngle: 2.0 * .pi,
  clockwise: false)

您已將中心點(diǎn)更改為(10, 10)而不是(20, 20)

運(yùn)行playground玉吁。 由于圓心的偏移照弥,你又回到了整個(gè)圓:

圖案單元格邊界也與圓完美匹配,導(dǎo)致每個(gè)單元格與其他單元格相鄰进副。

8. Applying a Transformation to the Pattern

您可以通過許多有趣的方式轉(zhuǎn)換您的圖案这揣。 在 draw(_:)中悔常,將 pattern 替換為以下內(nèi)容:

// 1
let transform = CGAffineTransform(translationX: 5, y: 5)
// 2
guard let pattern = CGPattern(
  info: nil,
  bounds: CGRect(x: 0, y: 0, width: 20, height: 20),
  matrix: transform,
  xStep: 20,
  yStep: 20,
  tiling: .constantSpacing,
  isColored: true,
  callbacks: &callbacks)
else { return }

您已經(jīng)通過向它傳遞一個(gè)轉(zhuǎn)換矩陣來(lái)修改圖案。 這是一個(gè)仔細(xì)的說明:

  • 1) 創(chuàng)建表示平移的仿射變換矩陣(affine transformation matrix)给赞。
  • 2) 通過在矩陣(matrix)中傳遞它來(lái)配置圖案以使用此轉(zhuǎn)換机打。

運(yùn)行playground。 請(qǐng)注意圖案如何向右和向下移動(dòng)以匹配您定義的翻譯:

除了變換您的圖案片迅,您還可以縮放和旋轉(zhuǎn)圖案單元格残邀。 稍后在為游戲應(yīng)用程序構(gòu)建圖案時(shí),您將看到如何旋轉(zhuǎn)圖案柑蛇。

9. Adding Fill and Stroke to the Pattern

這是填充和描邊彩色圖案的方法芥挣。 在 drawPattern 中,替換設(shè)置填充顏色的行并使用以下內(nèi)容填充路徑:

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

在這里耻台,您將填充顏色更改為黃色并設(shè)置描邊顏色空免。 然后使用填充和描邊路徑的選項(xiàng)調(diào)用 drawPath(using:)

運(yùn)行您的playground并檢查圖案現(xiàn)在是否顯示了您的新填充顏色和描邊:

到目前為止盆耽,您已經(jīng)使用了彩色圖案蹋砚,并在圖案繪制方法中定義了顏色。 在完成的游戲中摄杂,您必須創(chuàng)建具有不同顏色的圖案坝咐。 你可能意識(shí)到為每種顏色編寫一個(gè)繪制方法并不是要走的路。 這就是masking patterns發(fā)揮作用的地方匙姜。


Using Masking Patterns

Masking patterns在圖案單元格繪制方法之外定義其顏色信息畅厢。 這允許您更改圖案顏色以滿足您的需要。

下面是一個(gè)沒有顏色關(guān)聯(lián)的masking pattern的例子:

圖案到位后氮昧,您現(xiàn)在可以應(yīng)用顏色框杜。 下面的第一個(gè)示例顯示應(yīng)用于mask的藍(lán)色,第二個(gè)示例顯示橙色:

現(xiàn)在袖肥,您要將一直使用的圖案更改為masking pattern咪辱。

drawPattern 中刪除設(shè)置填充和描邊顏色的代碼并繪制路徑并將其替換為以下內(nèi)容:

context.fillPath()

這會(huì)將代碼恢復(fù)為填充路徑。

pattern替換為以下內(nèi)容:

guard 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)
else { return }

這將isColored 設(shè)置為 false椎组,將您的圖案更改為masking pattern油狂。 您還已將垂直和水平間距增加到25。現(xiàn)在寸癌,您需要提供圖案的色彩空間信息专筷。

將現(xiàn)有的 patternSpace 分配替換為以下內(nèi)容:

let baseSpace = CGColorSpaceCreateDeviceRGB()
guard let patternSpace = CGColorSpace(patternBaseSpace: baseSpace)
else { return }

在這里,您可以獲得對(duì)標(biāo)準(zhǔn)設(shè)備相關(guān) RGB 顏色空間的引用蒸苇。 然后您將圖案顏色空間更改為此值而不是之前的 nil 值磷蛹。

在其下方,替換您創(chuàng)建 alpha 的行并使用以下內(nèi)容設(shè)置上下文填充模式:

let fillColor: [CGFloat] = [0.0, 1.0, 1.0, 1.0]
context.setFillPattern(pattern, colorComponents: fillColor)

這會(huì)在填充圖案時(shí)創(chuàng)建應(yīng)用于蒙版下方的顏色溪烤。

運(yùn)行playground味咳。 您的圖案顏色會(huì)更新以反映您在 draw 方法之外配置的青色設(shè)置:

1. Stroking and Filling the Masking Pattern

現(xiàn)在庇勃,是時(shí)候描邊和填充遮罩圖案了。 這就像描邊彩色圖案槽驶。

drawPattern 中的 context.fillPath()行替換為以下內(nèi)容:

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

盡管您在 draw(_:)內(nèi)設(shè)置了描邊顏色责嚷,但您的圖案顏色仍然在方法外設(shè)置。

運(yùn)行 Playground 以查看描邊圖案:

您現(xiàn)在已經(jīng)積累了使用不同圖案配置和遮罩圖案的經(jīng)驗(yàn)掂铐。 您可以開始構(gòu)建Recall所需的圖案罕拂。


Creating the Game Pattern

將以下代碼添加到 Playground 的頂部:

extension CGPath {
  // 1
  static func triangle(in rect: CGRect) -> CGPath {
    let path = CGMutablePath()
    // 2
    let top = CGPoint(x: rect.width / 2, y: 0)
    let bottomLeft = CGPoint(x: 0, y: rect.height)
    let bottomRight = CGPoint(x: rect.width, y: rect.height)
    // 3
    path.addLines(between: [top, bottomLeft, bottomRight])
    // 4
    path.closeSubpath()
    return path
  }
}

通過代碼,一步一步:

  • 1) 擴(kuò)展 CGPath 以創(chuàng)建三角形路徑堡纬。
  • 2) 指定構(gòu)成三角形的三個(gè)點(diǎn)聂受。
  • 3) 在點(diǎn)之間添加線。
  • 4) 關(guān)閉路徑烤镐。

然后在 PatternView 中,添加以下空枚舉:

enum Constants {
  static let patternSize: CGFloat = 30.0
  static let patternRepeatCount: CGFloat = 2
}

這些代表您在設(shè)置圖案時(shí)將使用的常量棍鳖。 patternSize 定義模式單元格大小炮叶, patternRepeatCount定義模式視圖中模式單元格的數(shù)量。

1. Drawing a Triangle

Constants 定義之后添加以下內(nèi)容:

let drawTriangle: CGPatternDrawPatternCallback = { _, context in
  let trianglePath = CGPath.triangle(in:
    CGRect(
      x: 0,
      y: 0,
      width: Constants.patternSize,
      height: Constants.patternSize))
  context.addPath(trianglePath)
  context.fillPath()
}

這定義了用于繪制三角形圖案的新回調(diào)渡处。 在其中镜悉,您調(diào)用CGPath.triangle(in :)返回表示三角形的路徑。 然后在填充之前將此路徑添加到上下文中医瘫。

請(qǐng)注意侣肄,閉包沒有指定填充顏色,因此它可以是一個(gè)遮罩圖案醇份。

draw(_:)中稼锅,將回調(diào)callbacks更改為以下內(nèi)容:

var callbacks = CGPatternCallbacks(
  version: 0, 
  drawPattern: drawTriangle, 
  releaseInfo: nil)

您現(xiàn)在正在使用三角形繪制callback

2. Drawing Repeating Triangles as a Pattern

刪除 drawPattern僚纷,因?yàn)樗辉傩枰恕?/p>

另外矩距,在draw(_ :)中,將分配transformpattern的代碼替換為以下代碼:

// 1
let patternStepX = rect.width / Constants.patternRepeatCount
let patternStepY = rect.height / Constants.patternRepeatCount
// 2
let patternOffsetX = (patternStepX - Constants.patternSize) / 2.0
let patternOffsetY = (patternStepY - Constants.patternSize) / 2.0
// 3
let transform = CGAffineTransform(
  translationX: patternOffsetX,
  y: patternOffsetY)
// 4
guard 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)
else { return }

這是該代碼的作用怖竭,一步一步:

  • 1) 使用視圖的寬度和高度以及視圖中的模式單元數(shù)來(lái)計(jì)算水平和垂直步長(zhǎng)锥债。
  • 2) 計(jì)算尺寸以在其邊界內(nèi)水平和垂直居中模式單元格。
  • 3) 根據(jù)您定義的居中變量設(shè)置CGAffineTransform 轉(zhuǎn)換痊臭。
  • 4) 根據(jù)您計(jì)算出的參數(shù)創(chuàng)建圖案對(duì)象哮肚。

運(yùn)行playground。 您將看到四個(gè)三角形广匙,每個(gè)三角形都在其邊界內(nèi)垂直和水平居中:

接下來(lái)允趟,您將獲得與 Recall 應(yīng)用程序更加匹配的背景顏色。

MyViewController中艇潭,如下更改loadView()中的背景色設(shè)置:

view.backgroundColor = .lightGray

接下來(lái)拼窥,轉(zhuǎn)到 PatternView 并更改 draw(_:)中的上下文填充設(shè)置戏蔑,如下所示:

UIColor.white.setFill()

運(yùn)行playground。 你的主視圖的背景現(xiàn)在應(yīng)該是灰色的鲁纠,圖案視圖的背景是白色的:

3. Customizing the Pattern View

現(xiàn)在您已正確顯示基本圖案总棵,您可以進(jìn)行更改以控制圖案方向。

Constants之后的 PatternView 頂部附近添加以下枚舉:

enum PatternDirection: CaseIterable {
  case left
  case top
  case right
  case bottom
}

這表示三角形可以指向的不同方向改含。 它們與您的入門應(yīng)用程序中的方向相匹配情龄。

將以下屬性添加到 PatternView

var fillColor: [CGFloat] = [1.0, 0.0, 0.0, 1.0]
var direction: PatternDirection = .top

這表示您將應(yīng)用于遮罩圖案和圖案方向的顏色。 該類設(shè)置默認(rèn)顏色為紅色(四個(gè)數(shù)組分量代表紅色捍壤、綠色骤视、藍(lán)色和alpha)和默認(rèn)方向?yàn)轫敳俊?/p>

刪除在 draw(_:) 底部附近找到的局部 fillColor 聲明。 這將確保您改用實(shí)例屬性鹃觉。

transform替換為以下內(nèi)容:

// 1
var transform: CGAffineTransform
// 2
switch direction {
case .top:
  transform = .identity
case .right:
  transform = CGAffineTransform(rotationAngle: 0.5 * .pi)
case .bottom:
  transform = CGAffineTransform(rotationAngle: .pi)
case .left:
  transform = CGAffineTransform(rotationAngle: 1.5 * .pi)
}
// 3
transform = transform.translatedBy(x: patternOffsetX, y: patternOffsetY)

這是剛剛發(fā)生的事情:

  • 1) 為您的pattern transform聲明一個(gè) CGAffineTransform變量专酗。
  • 2) 如果圖案方向是top,則將變換分配為單位矩陣盗扇。 否則祷肯,變換是基于方向的旋轉(zhuǎn)。 例如疗隶,如果圖案指向右側(cè)佑笋,則旋轉(zhuǎn)為 π / 2 弧度或順時(shí)針 90o
  • 3) 應(yīng)用 CGAffineTransform 轉(zhuǎn)換以在其邊界內(nèi)居中pattern cell斑鼻。

運(yùn)行playground蒋纬。 根據(jù)您的默認(rèn)圖案填充顏色,您的三角形是紅色的:

4. Changing the Pattern’s Color and Direction

現(xiàn)在是設(shè)置代碼來(lái)控制和測(cè)試圖案顏色和方向的好時(shí)機(jī)坚弱。

在屬性定義后的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)
}

這將設(shè)置一個(gè)初始化器蜀备,該初始化器采用填充顏色和圖案方向。 direction有一個(gè)默認(rèn)值史汗。

您還為storyboard初始化視圖添加了所需的初始化程序琼掠。 稍后當(dāng)您將代碼傳輸?shù)綉?yīng)用程序時(shí),您將需要它停撞。

MyViewController 中瓷蛙,更改patternView,因?yàn)槟牧顺跏蓟鳎?/p>

let patternView = PatternView(
  fillColor: [0.0, 1.0, 0.0, 1.0],
  direction: .right)

在這里戈毒,您使用非默認(rèn)值實(shí)例化模式視圖艰猬。

運(yùn)行playground。 您的三角形現(xiàn)在是綠色并指向右側(cè):

恭喜埋市! 你已經(jīng)使用 Playgrounds 完成了圖案的原型設(shè)計(jì)冠桃。 是時(shí)候在 Recall 中使用這種模式了。

5. Updating the Game to Use the Pattern

打開 Recall 啟動(dòng)項(xiàng)目道宅。 轉(zhuǎn)到 PatternView.swift 并將 CGPath 擴(kuò)展名從您的playground復(fù)制到文件末尾食听。

接下來(lái)胸蛛,將 PatternView.swift 中的 PatternView 替換為您的 Playground 中的類。

注意:您可以使用 Xcode 的代碼折疊功能極大地簡(jiǎn)化此過程樱报。 在 Playground 中葬项,將光標(biāo)放在class PatternView: UIView {的左大括號(hào)之后,然后從菜單中選擇 Editor ? Code Folding ? Fold迹蛤。 三擊生成的折疊線以選擇整個(gè)類民珍,然后按 Command-C。 在項(xiàng)目中盗飒,重復(fù)折疊和選擇類的過程嚷量。 按 Command-V 替換它。

構(gòu)建并運(yùn)行應(yīng)用程序逆趣。 您應(yīng)該會(huì)看到類似以下內(nèi)容的內(nèi)容:

有些事情不太對(duì)勁蝶溶。 您的圖案似乎卡在默認(rèn)模式下。 看起來(lái)新的游戲視圖沒有刷新圖案視圖汗贫。

轉(zhuǎn)到 GameViewController.swift 并將以下內(nèi)容添加到 setupPatternView(_:towards:sharingColor:)的末尾:

patternView.setNeedsDisplay()

這會(huì)提示系統(tǒng)重新繪制圖案身坐,以便它選取新的圖案信息。

構(gòu)建并運(yùn)行應(yīng)用程序落包。 您現(xiàn)在應(yīng)該看到顏色和方向很好地混合在一起:

點(diǎn)擊其中一個(gè)答案按鈕并玩游戲以檢查一切是否按預(yù)期進(jìn)行。

恭喜您完成Recall摊唇! 從簡(jiǎn)單的油漆工作令人麻木的日子里咐蝇,您已經(jīng)走了很長(zhǎng)一段路。


Considering Performance When Using Patterns

Core Graphics圖案真的很快巷查。您可以使用以下幾個(gè)選項(xiàng)來(lái)繪制圖案:

  • 1) 使用您在本教程中使用的 Core Graphics pattern APIs有序。
  • 2) 使用 UIKit 包裝方法,例如 UIColor(patternImage:)岛请。
  • 3) 在具有許多 Core Graphics 調(diào)用的循環(huán)中繪制所需的圖案旭寿。

如果您的模式只繪制一次,則 UIKit 包裝器方法是最簡(jiǎn)單的崇败。它的性能也應(yīng)該與較低級(jí)別的 Core Graphics 調(diào)用相媲美盅称。何時(shí)使用它的一個(gè)示例是繪制靜態(tài)背景圖案。

與運(yùn)行在主線程上的 UIKit 不同后室,Core Graphics 可以在后臺(tái)線程中工作缩膝。 Core Graphics 圖案在處理復(fù)雜的繪圖或動(dòng)態(tài)圖案時(shí)性能更高。

在循環(huán)中繪制圖案是最慢的選擇岸霹。Core Graphics pattern進(jìn)行一次繪制調(diào)用并緩存結(jié)果疾层,使其更高效。

您現(xiàn)在應(yīng)該對(duì)如何使用 Core Graphics 來(lái)創(chuàng)建圖案有一個(gè)深入的了解贡避。查看 Patterns and Playgrounds教程痛黎,了解如何使用 UIKit構(gòu)建模式予弧。

您可能還想通讀Curves and Layers教程,該教程側(cè)重于繪制各種曲線并使用layer克隆它們湖饱。

后記

本篇主要講述了以高效的方式繪制圖案掖蛤,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市琉历,隨后出現(xiàn)的幾起案子坠七,更是在濱河造成了極大的恐慌,老刑警劉巖旗笔,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彪置,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蝇恶,警方通過查閱死者的電腦和手機(jī)拳魁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撮弧,“玉大人潘懊,你說我怎么就攤上這事』哐埽” “怎么了授舟?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贸辈。 經(jīng)常有香客問我释树,道長(zhǎng),這世上最難降的妖魔是什么擎淤? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任奢啥,我火速辦了婚禮,結(jié)果婚禮上嘴拢,老公的妹妹穿的比我還像新娘桩盲。我一直安慰自己,他們只是感情好席吴,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布赌结。 她就那樣靜靜地躺著,像睡著了一般抢腐。 火紅的嫁衣襯著肌膚如雪姑曙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天迈倍,我揣著相機(jī)與錄音伤靠,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宴合,可吹牛的內(nèi)容都是我干的焕梅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼卦洽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贞言!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起阀蒂,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤该窗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蚤霞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酗失,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年昧绣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了规肴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夜畴,死狀恐怖拖刃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贪绘,我是刑警寧澤兑牡,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站税灌,受9級(jí)特大地震影響发绢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垄琐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望经柴。 院中可真熱鬧狸窘,春花似錦、人聲如沸坯认。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牛哺。三九已至陋气,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間引润,已是汗流浹背巩趁。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淳附,地道東北人议慰。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓蠢古,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親别凹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子草讶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容