版本記錄
版本號 | 時(shí)間 |
---|---|
V1.0 | 2018.10.21 星期日 |
前言
quartz
是一個(gè)通用的術(shù)語,用于描述在iOS
和MAC OS X
中整個(gè)媒體層用到的多種技術(shù) 包括圖形、動(dòng)畫且轨、音頻、適配虚婿。Quart 2D
是一組二維繪圖和渲染API
旋奢,Core Graphic
會(huì)使用到這組API
,Quartz Core
專指Core Animation
用到的動(dòng)畫相關(guān)的庫然痊、API
和類至朗。CoreGraphics
是UIKit
下的主要繪圖系統(tǒng),頻繁的用于繪制自定義視圖剧浸。Core Graphics
是高度集成于UIView
和其他UIKit
部分的锹引。Core Graphics
數(shù)據(jù)結(jié)構(gòu)和函數(shù)可以通過前綴CG
來識別。在app中很多時(shí)候繪圖等操作我們要利用CoreGraphic
框架唆香,它能繪制字符串嫌变、圖形、漸變色等等躬它,是一個(gè)很強(qiáng)大的工具腾啥。感興趣的可以看我另外幾篇。
1. CoreGraphic框架解析(一)—— 基本概覽
2. CoreGraphic框架解析(二)—— 基本使用
3. CoreGraphic框架解析(三)—— 類波浪線的實(shí)現(xiàn)
4. CoreGraphic框架解析(四)—— 基本架構(gòu)補(bǔ)充
開始
首先看一下寫作環(huán)境
Swift 4, iOS 11, Xcode 9
想象一下,你已經(jīng)完成了你的應(yīng)用程序倘待,它工作正常疮跑,但界面缺乏風(fēng)格。 您可以在Photoshop中繪制所有自定義控件圖像的幾種尺寸延柠,并希望Apple不會(huì)出現(xiàn)@ 4x視網(wǎng)膜屏幕...或者祸挪,您可以提前思考并使用Core Graphics
在代碼中創(chuàng)建一個(gè)圖像,可以清晰地縮放任何圖像設(shè)備尺寸贞间。
Core Graphics是Apple的矢量繪圖框架 - 它是一個(gè)強(qiáng)大而強(qiáng)大的API,需要學(xué)習(xí)很多東西雹仿。 但是從不擔(dān)心 - 這幾篇教程將通過簡單的開始讓您輕松進(jìn)入它增热,最后您將能夠創(chuàng)建可在您的應(yīng)用中使用的令人驚嘆的圖形。
這是一個(gè)全新的系列胧辽,采用現(xiàn)代方法教授Core Graphics峻仇。 該系列還包括@IBDesignable
和@IBInspectable
等酷炫功能,使學(xué)習(xí)Core Graphics變得輕松有趣邑商。
是時(shí)候開始吧摄咆!
Introducing Flo – One glass at a time
您將創(chuàng)建一個(gè)完整的應(yīng)用程序來跟蹤您的飲水習(xí)慣。
具體而言人断,它可以輕松跟蹤您喝多少水吭从。 “他們”告訴我們,每天喝八杯水是健康的恶迈,但幾杯后很容易失去跟蹤涩金。 這就是Flo
的用武之地;每當(dāng)你喝掉一杯清爽的水暇仲,點(diǎn)擊柜臺步做。 您還會(huì)看到之前七天消費(fèi)的圖表。
在本系列的第一部分中奈附,您將使用UIKit的繪圖方法創(chuàng)建三個(gè)控件全度。
然后在第二部分中,您將深入了解Core Graphics上下文并繪制圖形斥滤。
在第三部分中将鸵,您將創(chuàng)建一個(gè)帶圖案的背景,并為自己頒發(fā)一張自制的Core Graphics獎(jiǎng)牌中跌。
您的首要任務(wù)是創(chuàng)建自己的Flo
應(yīng)用程序咨堤。 沒有下載可以讓你前進(jìn),因?yàn)槿绻銖念^開始構(gòu)建它漩符,你會(huì)學(xué)到更多一喘。
創(chuàng)建一個(gè)新項(xiàng)目(File \ New \ Project ...)
,選擇模板iOS \ Application \ Single View App
并單擊Next。
填寫項(xiàng)目選項(xiàng)凸克。 將Product Name
設(shè)置為Flo
议蟆,將語言設(shè)置為Swift
,然后單擊Next
萎战。
在最后一個(gè)屏幕上咐容,取消選中Create Git repository
并單擊Create
。
您現(xiàn)在有一個(gè)帶有故事板和視圖控制器的初學(xué)者項(xiàng)目蚂维。
Custom Drawing on Views - 視圖上的自定義繪圖
自定義繪圖有三個(gè)步驟:
- 1) 創(chuàng)建一個(gè)UIView子類戳粒。
- 2) 覆蓋
draw(_ :)
并添加一些Core Graphics
繪圖代碼。 - 3) 沒有第3步 - 就是這樣虫啥!
你可以通過制作一個(gè)自定義繪制的加號按鈕來嘗試這個(gè)蔚约,如下所示:
創(chuàng)建一個(gè)新文件(File \ New \ File ...)
,選擇iOS \ Source \ Cocoa Touch Class
涂籽,單擊Next
苹祟。 在此屏幕中,將新類命名為PushButton
评雌,使其成為UIButton
的子類树枫,并確保語言為Swift
。 單擊Next
景东,然后單擊Create
砂轻。
UIButton是UIView的子類,因此UIView中的所有方法(例如draw(_ :)
)也可以在UIButton中使用耐薯。
在Main.storyboard
中舔清,將UIButton拖到視圖控制器的視圖中,然后選擇Document Outline
中的按鈕曲初。
在Identity Inspector
中体谒,更改類以使用您自己的PushButton
。
1. Auto Layout Constraints - 自動(dòng)布局約束
現(xiàn)在臼婆,您將設(shè)置自動(dòng)布局約束(文本說明如下):
- 1) 選中該按鈕后抒痒,按住Control鍵從按鈕中心稍微向左拖動(dòng)(仍然在按鈕內(nèi)),然后從彈出菜單中選擇
Width
颁褂。 - 2) 同樣故响,選擇按鈕后,按住Control鍵從按鈕中心稍微向上控制 - 拖動(dòng)(仍然在按鈕內(nèi))颁独,然后從彈出菜單中選擇
Height
彩届。 - 3) 按住Control鍵從按鈕內(nèi)部向左拖動(dòng)到按鈕外部,然后選擇
Center Vertically in Safe Area
誓酒。 - 4) 最后按住Control鍵從按鈕內(nèi)部向上拖動(dòng)到按鈕外部樟蠕,然后選擇
Center Horizontally in Safe Area
贮聂。
這將創(chuàng)建四個(gè)必需的自動(dòng)布局約束;您現(xiàn)在可以在Size Inspector
中看到它們:
單擊Align center Y
約束上的Edit
寨辩,并將其常量設(shè)置為100吓懈。這會(huì)將按鈕的垂直位置從中心移動(dòng)到中心下方的100個(gè)點(diǎn)。 將Width
和Height
約束常量更改為等于100靡狞。 最終約束應(yīng)如下所示:
在Attributes Inspector
中耻警,刪除默認(rèn)標(biāo)題Button
。
如果您愿意甸怕,可以在此時(shí)構(gòu)建和運(yùn)行甘穿,但是現(xiàn)在您只能看到一個(gè)空白屏幕。 是時(shí)候解決這個(gè)問題了梢杭!
Drawing the Button - 繪制按鈕
回想一下你想要制作的按鈕是圓形的:
要在Core Graphics
中繪制形狀扒磁,您可以定義一條路徑,告訴Core Graphics要跟蹤的線(如加號的兩條直線)或要填充的線(如此處應(yīng)填充的圓)式曲。 如果您熟悉Photoshop中的Illustrator
或矢量形狀,那么您將很容易理解路徑缸榛。
關(guān)于路徑有三個(gè)基本要點(diǎn):
- 可以描邊和填充路徑吝羞。
- stroke概述了當(dāng)前描邊顏色的路徑。
- 填充將填充具有當(dāng)前填充顏色的閉合路徑内颗。
創(chuàng)建Core Graphics路徑的一種簡單方法是通過一個(gè)名為UIBezierPath
的便捷類钧排。 這使您可以使用用戶友好的API輕松創(chuàng)建路徑,無論您是要基于線均澳,曲線恨溜,矩形還是一系列連接點(diǎn)創(chuàng)建路徑。
嘗試使用UIBezierPath
創(chuàng)建路徑找前,然后用綠色填充它糟袁。 為此,請打開PushButton.swift
并添加此方法:
override func draw(_ rect: CGRect) {
let path = UIBezierPath(ovalIn: rect)
UIColor.green.setFill()
path.fill()
}
首先躺盛,創(chuàng)建一個(gè)橢圓形的UIBezierPath
项戴,它是傳遞給它的矩形的大小。 在這種情況下槽惫,它將是您在故事板中定義的100×100
按鈕的大小周叮,因此“橢圓”實(shí)際上將是一個(gè)圓圈。
路徑本身不會(huì)繪制任何東西界斜。 您可以定義沒有可用繪圖上下文的路徑仿耽。 要繪制路徑,請?jiān)诋?dāng)前上下文中設(shè)置填充顏色(下面有更多內(nèi)容)各薇,然后填充路徑项贺。
構(gòu)建并運(yùn)行應(yīng)用程序,您將看到綠色圓圈。
到目前為止敬扛,您已經(jīng)發(fā)現(xiàn)制作自定義形狀的視圖是多么容易晰洒。您已經(jīng)通過創(chuàng)建UIButton子類,覆蓋draw(_ :)
并將UIButton添加到故事板來完成此操作啥箭。
Behind the Scenes in Core Graphics - 在Core Graphics的幕后
每個(gè)UIView都有一個(gè)圖形上下文(context)
谍珊,視圖的所有繪圖在傳輸?shù)皆O(shè)備的硬件之前呈現(xiàn)在此上下文中。
每當(dāng)視圖需要更新時(shí)急侥,iOS都會(huì)通過調(diào)用draw(_ :)
來更新上下文砌滞。這種情況發(fā)生在:
- 該視圖是屏幕上的新視圖。
- 它頂部的視圖被移動(dòng)了坏怪。
- 視圖的
hidden
屬性已更改贝润。 - 您的應(yīng)用程序顯式調(diào)用視圖上的
setNeedsDisplay()
或setNeedsDisplayInRect()
方法。
注意:在
draw(_ :)
中完成的任何繪圖都會(huì)進(jìn)入視圖的圖形上下文铝宵。請注意打掘,如果您在draw(_ :)
之外開始繪制繪圖,正如您將在本教程的最后部分所做的那樣鹏秋,您將必須創(chuàng)建自己的圖形上下文尊蚁。
您還沒有在本教程中使用過Core Graphics
,因?yàn)閁IKit包含許多Core Graphics函數(shù)的包裝器侣夷。例如横朋,UIBezierPath
是CGMutablePath
的包裝器,CGMutablePath
是較低級別的Core Graphics API
百拓。
注意:永遠(yuǎn)不要直接調(diào)用
draw(_ :)
琴锭。如果您的視圖未更新,請?jiān)谝晥D上調(diào)用setNeedsDisplay()
衙传。
setNeedsDisplay()
本身不調(diào)用draw(_ :)
决帖,但它將視圖標(biāo)記為'dirty'
,在下一個(gè)屏幕更新周期使用draw(_ :)
觸發(fā)重繪粪牲。即使你在同一個(gè)方法中調(diào)用setNeedsDisplay()
五次古瓤,你也只能實(shí)際調(diào)用draw(_ :)
一次。
@IBDesignable – Interactive Drawing - 交互式繪圖
創(chuàng)建代碼來繪制路徑腺阳,然后運(yùn)行應(yīng)用程序以查看它看起來像油漆干燥一樣令人興奮落君,但你有選擇。 實(shí)時(shí)渲染(Live Rendering)
允許視圖通過運(yùn)行draw(_ :)
方法在故事板中更準(zhǔn)確地繪制自己亭引。 更重要的是绎速,故事板將立即更新為draw(_ :)
中的更改。 您只需要一個(gè)屬性焙蚓!
仍然在PushButton.swift
中纹冤,就在類聲明之前洒宝,添加:
@IBDesignable
這就是啟用實(shí)時(shí)渲染所需的全部內(nèi)容。 回到Main.storyboard
并注意到萌京,現(xiàn)在雁歌,您的按鈕顯示為綠色圓圈葬毫,就像您構(gòu)建和運(yùn)行時(shí)一樣掖肋。
現(xiàn)在設(shè)置你的屏幕,以便你有故事板和代碼并排唉工。
通過選擇PushButton.swift
顯示代碼來執(zhí)行此操作求妹,然后在右上角單擊Assistant Editor
- 看起來像兩個(gè)交織在一起的環(huán)的圖標(biāo)乏盐。 然后故事板應(yīng)顯示在右側(cè)窗格中。 如果沒有制恍,則必須在窗格頂部的痕跡路徑中選擇故事板:
關(guān)閉故事板左側(cè)的文檔大綱以釋放一些空間父能。 通過拖動(dòng)文檔大綱窗格的邊緣或單擊故事板底部的按鈕來執(zhí)行此操作:
完成所有操作后,您的屏幕應(yīng)如下所示:
在PushButton
的draw(_:)
中净神,改變
UIColor.green.setFill()
為
UIColor.blue.setFill()
你會(huì)(幾乎)立即看到故事板中的變化何吝。 太酷了!
現(xiàn)在鹃唯,您將為加號創(chuàng)建行岔霸。
Drawing Into the Context - 繪制到上下文
Core Graphics使用“繪制模型(painter’s model)
”。 當(dāng)你畫一個(gè)上下文時(shí)俯渤,它幾乎就像畫一幅畫。 你鋪設(shè)一條路并填充它型宝,然后在頂部另一條路徑上鋪設(shè)路徑并填滿它八匠。 您無法更改已放置的像素,但可以在上面覆蓋“繪制”它們趴酣。
Apple的文檔中的這張圖片描述了它的工作原理梨树。 正如您在畫布上繪畫時(shí)一樣,繪制的順序至關(guān)重要岖寞。
你的加號是在藍(lán)色圓圈的頂部抡四,所以首先你編碼藍(lán)色圓圈然后加號。
您可以為加號繪制兩個(gè)矩形仗谆,但是更容易繪制路徑然后用所需的厚度描邊它指巡。
在PushButton
中添加此結(jié)構(gòu)和這些常量:
private struct Constants {
static let plusLineWidth: CGFloat = 3.0
static let plusButtonScale: CGFloat = 0.6
static let halfPointShift: CGFloat = 0.5
}
private var halfWidth: CGFloat {
return bounds.width / 2
}
private var halfHeight: CGFloat {
return bounds.height / 2
}
現(xiàn)在在draw(_ :)
方法的末尾添加此代碼以繪制加號的水平短劃線:
//set up the width and height variables
//for the horizontal stroke
let plusWidth: CGFloat = min(bounds.width, bounds.height) * Constants.plusButtonScale
let halfPlusWidth = plusWidth / 2
//create the path
let plusPath = UIBezierPath()
//set the path's line width to the height of the stroke
plusPath.lineWidth = Constants.plusLineWidth
//move the initial point of the path
//to the start of the horizontal stroke
plusPath.move(to: CGPoint(
x: halfWidth - halfPlusWidth,
y: halfHeight))
//add a point to the path at the end of the stroke
plusPath.addLine(to: CGPoint(
x: halfWidth + halfPlusWidth,
y: halfHeight))
//set the stroke color
UIColor.white.setStroke()
//draw the stroke
plusPath.stroke()
在此塊中,您設(shè)置UIBezierPath
隶垮,為其指定一個(gè)起始位置(圓圈的左側(cè))并繪制到結(jié)束位置(圓圈的右側(cè))藻雪。 然后用白色描繪路徑輪廓。 此時(shí)狸吞,您應(yīng)該在Storyboard
中看到這一點(diǎn):
在你的故事板中勉耀,你現(xiàn)在將有一個(gè)藍(lán)色圓圈指煎,中間有一個(gè)破折號:
注意:請記住,路徑只包含點(diǎn)便斥。 這是一個(gè)簡單的方法來掌握這個(gè)概念:創(chuàng)建路徑時(shí)想象你手中拿著筆至壤。 在頁面上放兩個(gè)點(diǎn),然后將筆放在起點(diǎn)枢纠,然后通過畫線畫一條線到下一個(gè)點(diǎn)像街。
這基本上就是你使用
move(to :)
和addLine(to :)
來處理上面的代碼。
現(xiàn)在在iPad 2或iPhone 6 Plus模擬器上運(yùn)行應(yīng)用程序京郑,你會(huì)發(fā)現(xiàn)破折號并不像應(yīng)該的那樣清晰宅广。 它有一條淡藍(lán)色的線環(huán)繞著它。
Points and Pixels - 點(diǎn)和像素
回到第一批iPhone的時(shí)代些举,點(diǎn)和像素占據(jù)了相同的空間并且大小相同跟狱,這使得它們基本上是相同的。 當(dāng)視網(wǎng)膜iPhone出現(xiàn)時(shí)户魏,屏幕上突然出現(xiàn)了相同數(shù)量點(diǎn)數(shù)的四倍像素驶臊。
同樣,iPhone 6 Plus再次增加了相同點(diǎn)的像素?cái)?shù)量叼丑。
注意:以下是概念性的 - 實(shí)際硬件像素可能不同关翎。 例如,在渲染3x后鸠信,iPhone 6 Plus會(huì)進(jìn)行縮減采樣以在屏幕上顯示完整圖像纵寝。 要了解有關(guān)iPhone 6 Plus下采樣的更多信息,請查看這篇精彩文章星立。
這是一個(gè)12×12像素的網(wǎng)格爽茴,其中的點(diǎn)以灰色和白色顯示。 第一個(gè)(iPad 2)是點(diǎn)到像素的直接映射绰垂。 第二個(gè)(iPhone 6)是2x視網(wǎng)膜屏幕室奏,其中一個(gè)點(diǎn)有4個(gè)像素,第三個(gè)(iPhone 6 Plus)是一個(gè)3x視網(wǎng)膜屏幕劲装,其中有一個(gè)點(diǎn)有9個(gè)像素胧沫。
你剛剛畫出的線高3點(diǎn)。 線條從路徑的中心開始劃線占业,因此在路徑中心線的兩側(cè)繪制1.5個(gè)點(diǎn)绒怨。
此圖顯示了在每個(gè)設(shè)備上繪制3點(diǎn)線。 您可以看到iPad 2和iPhone 6 Plus導(dǎo)致線條被劃分為半個(gè)像素 - 這當(dāng)然是無法完成的谦疾。 因此窖逗,iOS使用兩種顏色之間的顏色對半填充像素進(jìn)行反鋸齒處理,并且該線看起來模糊餐蔬。
實(shí)際上碎紊,iPhone 6 Plus擁有如此多的像素佑附,您可能不會(huì)注意到它的模糊性,盡管您應(yīng)該在設(shè)備上查看自己的應(yīng)用程序仗考。 但是音同,如果你正在開發(fā)像iPad 2或iPad mini這樣的非視網(wǎng)膜屏幕,你應(yīng)該盡一切可能避免抗鋸齒秃嗜。
如果你有奇怪的直線权均,你需要將它們放在正負(fù)0.5點(diǎn)以防止消除鋸齒。 如果你看一下上面的圖表锅锨,你會(huì)發(fā)現(xiàn)iPad 2上的一半點(diǎn)會(huì)將線條向上移動(dòng)半個(gè)像素叽赊,在iPhone 6上,向上移動(dòng)整個(gè)像素必搞,在iPhone 6 Plus上必指,向上移動(dòng)一個(gè)半像素。
在draw(_ :)
中恕洲,將move(to :)
和addLine(to :)
代碼行替換為:
//move the initial point of the path
//to the start of the horizontal stroke
plusPath.move(to: CGPoint(
x: halfWidth - halfPlusWidth + Constants.halfPointShift,
y: halfHeight + Constants.halfPointShift))
//add a point to the path at the end of the stroke
plusPath.addLine(to: CGPoint(
x: halfWidth + halfPlusWidth + Constants.halfPointShift,
y: halfHeight + Constants.halfPointShift))
iOS現(xiàn)在將在所有三個(gè)設(shè)備上邊緣清晰的渲染線條塔橡,因?yàn)槟F(xiàn)在將路徑移動(dòng)了半個(gè)點(diǎn)。
注意:對于像素完美線條霜第,您可以繪制和填充
UIBezierPath(rect :)
而不是線葛家,并使用視圖的contentScaleFactor
計(jì)算矩形的寬度和高度。 與從路徑中心向外繪制的筆劃不同泌类,填充僅在路徑內(nèi)繪制癞谒。
在前兩行代碼之后,在draw(_:)
中設(shè)置筆觸顏色之前刃榨,添加加號的垂直筆劃扯俱。 我敢打賭,你可以自己弄清楚如何做到這一點(diǎn)喇澡,因?yàn)槟阋呀?jīng)繪制了一個(gè)水平筆劃:
//Vertical Line
plusPath.move(to: CGPoint(
x: halfWidth + Constants.halfPointShift,
y: halfHeight - halfPlusWidth + Constants.halfPointShift))
plusPath.addLine(to: CGPoint(
x: halfWidth + Constants.halfPointShift,
y: halfHeight + halfPlusWidth + Constants.halfPointShift))
這與您用于在按鈕上繪制水平線的代碼基本相同。
您現(xiàn)在應(yīng)該在故事板中看到加號按鈕的實(shí)時(shí)渲染殊校。 這樣就完成了加號按鈕的繪制晴玖。
@IBInspectable – Custom Storyboard Properties - 自定義sb屬性
你需要為用戶提供一個(gè)減號按鈕。
減號按鈕與加號按鈕相同为流,只是它沒有垂直條并且顏色不同呕屎。 您將對減號按鈕使用相同的PushButton
類,并在將其添加到故事板時(shí)聲明它是什么類型的按鈕及其顏色敬察。
@IBInspectable
是一個(gè)可以添加到屬性的屬性秀睛,使Interface Builder
可以讀取它。 這意味著您將能夠在故事板中而不是在代碼中配置按鈕的顏色莲祸。
在PushButton
類的頂部蹂安,添加以下兩個(gè)屬性:
@IBInspectable var fillColor: UIColor = UIColor.green
@IBInspectable var isAddButton: Bool = true
更改draw(_:)
頂部的填充顏色代碼
UIColor.blue.setFill()
為
fillColor.setFill()
該故事板視圖中的按鈕將變?yōu)榫G色椭迎。
使用if語句將draw(_ :)
中的垂直行代碼包圍:
//Vertical Line
if isAddButton {
//vertical line code move(to:) and addLine(to:)
}
//existing code
//set the stroke color
UIColor.white.setStroke()
plusPath.stroke()
這使得只有在設(shè)置了isAddButton
時(shí)才繪制垂直線 - 這樣按鈕可以是加號或減號按鈕。
完成的PushButton
看起來像這樣:
import UIKit
@IBDesignable
class PushButton: UIButton {
private struct Constants {
static let plusLineWidth: CGFloat = 3.0
static let plusButtonScale: CGFloat = 0.6
static let halfPointShift: CGFloat = 0.5
}
private var halfWidth: CGFloat {
return bounds.width / 2
}
private var halfHeight: CGFloat {
return bounds.height / 2
}
@IBInspectable var fillColor: UIColor = UIColor.green
@IBInspectable var isAddButton: Bool = true
override func draw(_ rect: CGRect) {
let path = UIBezierPath(ovalIn: rect)
fillColor.setFill()
path.fill()
//set up the width and height variables
//for the horizontal stroke
let plusWidth: CGFloat = min(bounds.width, bounds.height) * Constants.plusButtonScale
let halfPlusWidth = plusWidth / 2
//create the path
let plusPath = UIBezierPath()
//set the path's line width to the height of the stroke
plusPath.lineWidth = Constants.plusLineWidth
//move the initial point of the path
//to the start of the horizontal stroke
plusPath.move(to: CGPoint(
x: halfWidth - halfPlusWidth + Constants.halfPointShift,
y: halfHeight + Constants.halfPointShift))
//add a point to the path at the end of the stroke
plusPath.addLine(to: CGPoint(
x: halfWidth + halfPlusWidth + Constants.halfPointShift,
y: halfHeight + Constants.halfPointShift))
if isAddButton {
//move the initial point of the path
//to the start of the horizontal stroke
plusPath.move(to: CGPoint(
x: halfWidth - halfPlusWidth + Constants.halfPointShift,
y: halfHeight + Constants.halfPointShift))
//add a point to the path at the end of the stroke
plusPath.addLine(to: CGPoint(
x: halfWidth + halfPlusWidth + Constants.halfPointShift,
y: halfHeight + Constants.halfPointShift))
}
//set the stroke color
UIColor.white.setStroke()
plusPath.stroke()
}
}
在故事板中田盈,選擇按鈕視圖畜号。 使用@IBInspectable
聲明的兩個(gè)屬性顯示在Attributes Inspector
的頂部:
將Fill Color
更改為RGB(87, 218, 213)
,并將Is Add Button
更改為off
允瞧。 通過轉(zhuǎn)到Fill Color\Other…\Color Sliders
并在顏色旁邊的每個(gè)輸入框中輸入值來更改顏色简软,所以它看起來像這樣:
更改將立即在故事板中進(jìn)行:
很酷,嗯述暂? 現(xiàn)在將Is Add Button
更改為on
痹升,將按鈕返回到加號按鈕。
A Second Button - 第二個(gè)按鈕
將新的UIButton添加到故事板并選擇它畦韭。 將其類更改為PushButton
疼蛾,就像使用上一個(gè)類一樣:
綠色加號按鈕將在您的舊加號按鈕下繪制。
在Attributes Inspector中廊驼,將Fill Color
更改為RGB(238,77,77)
并將Is Add Button
更改為off
据过。
刪除默認(rèn)標(biāo)題Button。
與以前的方式類似妒挎,為新視圖添加自動(dòng)布局約束:
- 選擇按鈕后绳锅,按住Control鍵從按鈕中心向左輕微拖動(dòng)(仍在按鈕內(nèi)),然后從彈出菜單中選擇
Width
酝掩。 - 同樣鳞芙,在選中按鈕的情況下,按住Control鍵從按鈕中心稍微向上拖動(dòng)(仍在按鈕內(nèi))期虾,然后從彈出菜單中選擇
Height
原朝。 - 控按住Control鍵從按鈕內(nèi)部向左拖動(dòng)到按鈕外部,然后選擇
Center Horizontally in Safe Area
镶苞。 - 按住Control鍵從底部按鈕向上拖動(dòng)到頂部按鈕喳坠,然后選擇
Vertical Spacing
。
添加約束后茂蚓,在Size Inspector
中編輯它們的常量值以匹配以下值:
構(gòu)建并運(yùn)行應(yīng)用程序壕鹉。 您現(xiàn)在擁有可重復(fù)使用的可自定義視圖,您可以將其添加到任何應(yīng)用程序中聋涨。 它在任何尺寸的設(shè)備上都清脆銳利晾浴。 這是在iPhone 4S上。
Arcs with UIBezierPath - 使用UIBezierPath的弧
您將創(chuàng)建的下一個(gè)自定義視圖是:
這看起來像一個(gè)填充的形狀牍白,但弧實(shí)際上只是一個(gè)胖的描邊路徑脊凰。 輪廓是由兩個(gè)弧組成的另一條描邊路徑。
創(chuàng)建一個(gè)新文件File \ New \ File ...
茂腥,選擇Cocoa Touch Class
狸涌,并將新類命名為CounterView
切省。 使它成為UIView的子類,并確保語言為Swift杈抢。 單擊Next数尿,然后單擊Create
。
將代碼替換為:
import UIKit
@IBDesignable class CounterView: UIView {
private struct Constants {
static let numberOfGlasses = 8
static let lineWidth: CGFloat = 5.0
static let arcWidth: CGFloat = 76
static var halfOfLineWidth: CGFloat {
return lineWidth / 2
}
}
@IBInspectable var counter: Int = 5
@IBInspectable var outlineColor: UIColor = UIColor.blue
@IBInspectable var counterColor: UIColor = UIColor.orange
override func draw(_ rect: CGRect) {
}
}
在這里惶楼,您還可以創(chuàng)建一個(gè)包含常量的結(jié)構(gòu)體右蹦。這些常數(shù)將在繪圖時(shí)使用,奇數(shù)一個(gè) - numberOfGlasses
- 是每天飲用的目標(biāo)水杯數(shù)歼捐。達(dá)到此數(shù)字時(shí)何陆,計(jì)數(shù)器將達(dá)到最大值。
您還可以創(chuàng)建三個(gè)可以在故事板中更新的@IBInspectable
屬性豹储。變量counter
跟蹤消耗的杯水?dāng)?shù)量贷盲,它是一個(gè)@IBDesignable
屬性,因?yàn)樗軌蛟诠适掳逯懈乃郏@對于測試計(jì)數(shù)器視圖非常有用巩剖。
轉(zhuǎn)到Main.storyboard
并在加上PushButton
上方添加一個(gè)UIView。與以前的方式類似钠怯,為新視圖添加自動(dòng)布局約束:
- 1) 選擇視圖后佳魔,按住Control鍵從按鈕中心稍微向左拖動(dòng)(仍然在視圖中),然后從彈出菜單中選擇
Width
晦炊。 - 2) 同樣鞠鲜,在選擇視圖的情況下,按住Control鍵從按鈕中心稍微向上(仍在視圖中)進(jìn)行控制 - 拖動(dòng)断国,然后從彈出菜單中選擇
Height
贤姆。 - 3) 按住Control鍵從視圖內(nèi)部向左拖動(dòng)到視圖外部,然后選擇
Center Horizontally in Safe Area
稳衬。 - 4) 按住Control鍵從視圖向下拖動(dòng)到頂部按鈕霞捡,然后選擇
Vertical Spacing
。
在Size Inspector
中編輯約束常量薄疚,如下所示:
在Identity Inspector
中碧信,將UIView的類更改為CounterView
。 您在draw(_ :)
中編碼的任何繪圖現(xiàn)在都會(huì)顯示在視圖中(但您還沒有添加任何圖形J涮椤)。
Impromptu Math Lesson - 即興數(shù)學(xué)課
我們暫時(shí)打斷了這個(gè)教程的簡短介紹慨畸,希望在高中水平數(shù)學(xué)方面不可怕莱坎。
上下文中的繪圖基于此單位圓。 單位圓是半徑為1.0的圓寸士。
紅色箭頭顯示弧的開始和結(jié)束位置檐什,以順時(shí)針方向繪制碴卧。 你將從3π/ 4弧度的位置繪制一個(gè)弧 - 相當(dāng)于135o,順時(shí)針到π/ 4弧度 - 即45o乃正。
弧度通常用于編程而不是度數(shù)住册,并且能夠以弧度進(jìn)行思考是有用的,這樣您每次想要使用圓時(shí)都不必轉(zhuǎn)換為度數(shù)瓮具。 稍后你需要弄清楚弧長荧飞,這是弧度發(fā)揮作用的時(shí)候。
單位圓中的圓弧長度(半徑為1.0)與弧度中的角度測量值相同名党。 例如叹阔,查看上圖,弧度從0o到90o的長度為π/ 2传睹。 要計(jì)算實(shí)際情況下弧的長度耳幢,請取單位圓弧長度并將其乘以實(shí)際半徑。
要計(jì)算上面紅色箭頭的長度欧啤,您只需要計(jì)算它跨越的弧度數(shù):
2π – end of arrow (3π/4) + point of arrow (π/4) = 3π/2
轉(zhuǎn)換為度數(shù)就是
360o – 135o + 45o = 270o
Back to Drawing Arcs - 回到繪制弧
在CounterView.swift
中睛藻,添加此代碼到draw(_:)
以繪制弧:
// 1
let center = CGPoint(x: bounds.width / 2, y: bounds.height / 2)
// 2
let radius: CGFloat = max(bounds.width, bounds.height)
// 3
let startAngle: CGFloat = 3 * .pi / 4
let endAngle: CGFloat = .pi / 4
// 4
let path = UIBezierPath(arcCenter: center,
radius: radius/2 - Constants.arcWidth/2,
startAngle: startAngle,
endAngle: endAngle,
clockwise: true)
// 5
path.lineWidth = Constants.arcWidth
counterColor.setStroke()
path.stroke()
下面進(jìn)行細(xì)分:
- 1) 定義視圖的中心點(diǎn)邢隧,您可以在其中旋轉(zhuǎn)圓弧店印。
- 2) 根據(jù)視圖的最大尺寸計(jì)算半徑。
- 3) 定義弧的起始角和終止角府框。
- 4) 根據(jù)剛剛定義的中心點(diǎn)吱窝,半徑和角度創(chuàng)建路徑。
- 5) 在最終描邊路徑之前設(shè)置線寬和顏色迫靖。
想象一下用指南針繪制它 - 你將指南針的點(diǎn)放在中心院峡,將手臂打開到你需要的半徑,用粗筆裝上它并旋轉(zhuǎn)它以畫出你的弧線系宜。
在此代碼中照激,center
是指南針的點(diǎn),radius
是指南針打開的寬度(減去筆寬度的一半)盹牧,弧寬是指筆的寬度俩垃。
在故事板中以及運(yùn)行應(yīng)用程序時(shí),您將看到以下內(nèi)容:
Outlining the Arc
當(dāng)用戶表示他們已經(jīng)享用了一杯水時(shí)汰寓,counter
上的輪廓顯示了朝向八杯水的目標(biāo)的進(jìn)展口柳。
該輪廓將包括兩個(gè)弧,一個(gè)外部和一個(gè)內(nèi)部有滑,以及兩條連接它們的線跃闹。
在CounterView.swift
中,將此代碼添加到draw(_:)
結(jié)束:
//Draw the outline
//1 - first calculate the difference between the two angles
//ensuring it is positive
let angleDifference: CGFloat = 2 * .pi - startAngle + endAngle
//then calculate the arc for each single glass
let arcLengthPerGlass = angleDifference / CGFloat(Constants.numberOfGlasses)
//then multiply out by the actual glasses drunk
let outlineEndAngle = arcLengthPerGlass * CGFloat(counter) + startAngle
//2 - draw the outer arc
let outlinePath = UIBezierPath(arcCenter: center,
radius: bounds.width/2 - Constants.halfOfLineWidth,
startAngle: startAngle,
endAngle: outlineEndAngle,
clockwise: true)
//3 - draw the inner arc
outlinePath.addArc(withCenter: center,
radius: bounds.width/2 - Constants.arcWidth + Constants.halfOfLineWidth,
startAngle: outlineEndAngle,
endAngle: startAngle,
clockwise: false)
//4 - close the path
outlinePath.close()
outlineColor.setStroke()
outlinePath.lineWidth = Constants.lineWidth
outlinePath.stroke()
這里要介紹幾件事:
- 1)
outlineEndAngle
是弧應(yīng)該結(jié)束的角度,使用當(dāng)前counter
值計(jì)算望艺。 - 2)
outlinePath
是外弧苛秕。 半徑被賦予UIBezierPath()
以計(jì)算弧的實(shí)際長度,因?yàn)樵摶〔皇菃挝粓A找默。 - 3) 向第一個(gè)弧添加內(nèi)弧艇劫。 它具有相同的角度但反向繪制(順時(shí)針設(shè)置為
false
)。 此外惩激,這會(huì)自動(dòng)在內(nèi)弧和外弧之間畫一條線店煞。 - 4) 關(guān)閉路徑會(huì)自動(dòng)在弧的另一端繪制一條線。
將CounterView.swift
中的counter
屬性設(shè)置為5
咧欣,您的CounterView
現(xiàn)在應(yīng)該在故事板中如下所示:
打開Main.storyboard
浅缸,選擇CounterView
,在Attributes Inspector
中魄咕,更改Counter
屬性以檢查繪圖代碼衩椒。 你會(huì)發(fā)現(xiàn)它是完全互動(dòng)的。 嘗試將計(jì)數(shù)器調(diào)整為大于8且小于零哮兰。 你稍后會(huì)解決這個(gè)問題毛萌。
將Counter Color
更改為RGB(87,218,213)
,并將Outline Color
更改為RGB(34,110,100)
喝滞。
Making it All Work
恭喜阁将! 你有控制,您所要做的就是將它們連接起來右遭,以便加號按鈕遞增計(jì)數(shù)器做盅,減號按鈕遞減計(jì)數(shù)器。
在Main.storyboard
中窘哈,將UILabel拖動(dòng)到Counter View
的中心吹榴,并確保它是Counter View的子視圖。 它在文檔大綱中看起來像這樣:
添加約束以垂直和水平居中標(biāo)簽滚婉。 最后图筹,標(biāo)簽應(yīng)該具有如下所示的約束:
在Attributes Inspector
中,將Alignment
更改為center
让腹,將font size
更改為36远剩,將默認(rèn)標(biāo)簽標(biāo)題更改為8。
轉(zhuǎn)到ViewController.swift
并將這些屬性添加到類的頂部:
//Counter outlets
@IBOutlet weak var counterView: CounterView!
@IBOutlet weak var counterLabel: UILabel!
仍然在ViewController.swift
中骇窍,將此方法添加到類的末尾:
@IBAction func pushButtonPressed(_ button: PushButton) {
if button.isAddButton {
counterView.counter += 1
} else {
if counterView.counter > 0 {
counterView.counter -= 1
}
}
counterLabel.text = String(counterView.counter)
}
在這里瓜晤,您可以根據(jù)按鈕的isAddButton
屬性遞增或遞減計(jì)數(shù)器,確保計(jì)數(shù)器不會(huì)降至零以下 - 沒有人可以喝負(fù)水腹纳。 您還可以更新標(biāo)簽中的計(jì)數(shù)器值痢掠。
還要將此代碼添加到viewDidLoad()
的末尾哈恰,以確保counterLabel
的初始值將更新:
counterLabel.text = String(counterView.counter)
在Main.storyboard
中,連接CounterView
outlet和UILabel outlet志群。 將方法連接到兩個(gè)PushButton
的Touch Up Inside
事件。
運(yùn)行該應(yīng)用程序蛔钙,看看您的按鈕是否更新了計(jì)數(shù)器標(biāo)簽锌云。 他們應(yīng)該。
但是等等吁脱,為什么計(jì)數(shù)器視圖不更新桑涎?
想想回到本教程的開頭,以及如何在移動(dòng)其上的其他視圖兼贡,或者更改其hidden
屬性攻冷,或者視圖是屏幕新視圖或應(yīng)用程序調(diào)用時(shí),視圖上的setNeedsDisplay()
或setNeedsDisplayInRect()
方法就會(huì)調(diào)用draw(_ :)
方法遍希。
但是等曼,只要計(jì)數(shù)器屬性更新,計(jì)數(shù)器視圖就需要更新凿蒜,否則用戶會(huì)認(rèn)為您的應(yīng)用程序已被破壞禁谦。
轉(zhuǎn)到CounterView.swift
并將counter
屬性聲明更改為:
@IBInspectable var counter: Int = 5 {
didSet {
if counter <= Constants.numberOfGlasses {
//the view needs to be refreshed
setNeedsDisplay()
}
}
}
此代碼使得僅當(dāng)計(jì)數(shù)器小于或等于用戶的目標(biāo)杯水?dāng)?shù)時(shí)視圖才會(huì)刷新,因?yàn)檩喞獌H上升到8废封。
再次運(yùn)行您的應(yīng)用州泊。 現(xiàn)在一切都應(yīng)該正常運(yùn)作。
后記
本篇主要講述了基于CoreGraphic的一個(gè)簡單繪制示例漂洋,感興趣的給個(gè)贊或者關(guān)注~~~