版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2019.02.14 星期四 |
前言
quartz
是一個(gè)通用的術(shù)語(yǔ)蒿叠,用于描述在iOS
和MAC OS X
中整個(gè)媒體層用到的多種技術(shù) 包括圖形咪鲜、動(dòng)畫(huà)、音頻栈顷、適配。Quart 2D
是一組二維繪圖和渲染API
嵌巷,Core Graphic
會(huì)使用到這組API
萄凤,Quartz Core
專(zhuān)指Core Animation
用到的動(dòng)畫(huà)相關(guān)的庫(kù)、API
和類(lèi)搪哪。CoreGraphics
是UIKit
下的主要繪圖系統(tǒng)靡努,頻繁的用于繪制自定義視圖。Core Graphics
是高度集成于UIView
和其他UIKit
部分的晓折。Core Graphics
數(shù)據(jù)結(jié)構(gòu)和函數(shù)可以通過(guò)前綴CG
來(lái)識(shí)別惑朦。在app中很多時(shí)候繪圖等操作我們要利用CoreGraphic
框架,它能繪制字符串漓概、圖形漾月、漸變色等等,是一個(gè)很強(qiáng)大的工具胃珍。感興趣的可以看我另外幾篇梁肿。
1. CoreGraphic框架解析(一)—— 基本概覽
2. CoreGraphic框架解析(二)—— 基本使用
3. CoreGraphic框架解析(三)—— 類(lèi)波浪線(xiàn)的實(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 (二)
開(kāi)始
首先看下寫(xiě)作環(huán)境
Swift 4.2, iOS 12, Xcode 10
Core Graphics是iOS開(kāi)發(fā)人員容易避免的框架之一蜓陌。 它有點(diǎn)模糊,語(yǔ)法不是超現(xiàn)代的栈雳,Apple并沒(méi)有像WWDC那樣給予它盡可能多的愛(ài)护奈! 另外缔莲,只需使用圖像即可輕松避免使用它哥纫。
但是,Core Graphics是一個(gè)非常強(qiáng)大的工具痴奏! 你可以擺脫平面設(shè)計(jì)師的束縛蛀骇,使用強(qiáng)大的CG劍自己創(chuàng)造出令人驚嘆的UI美。
在本教程中读拆,您將學(xué)習(xí)如何僅使用Core Graphics
創(chuàng)建可自定義擅憔,可重復(fù)使用的光澤按鈕。 難道你沒(méi)有聽(tīng)說(shuō)過(guò)skeuomorphism
的風(fēng)格嗎檐晕?
在此過(guò)程中暑诸,您將學(xué)習(xí)如何繪制圓角矩形,如何輕松地為Core Graphics繪圖著色以及如何創(chuàng)建漸變和光澤效果辟灰。
自定義UIButton有很多選項(xiàng)个榕,從完整的自定義UIButton
類(lèi)到較小的擴(kuò)展。 但是在這次討論中缺少的是一個(gè)詳細(xì)的核心圖形教程芥喇,用于自定義按鈕西采,從頭到尾。 這很簡(jiǎn)單继控;你可以使用它來(lái)獲得你想要的應(yīng)用程序的確切外觀械馆。
現(xiàn)在是時(shí)候開(kāi)始吧!
打開(kāi)啟動(dòng)項(xiàng)目:CoolButton Starter
武通。
項(xiàng)目結(jié)構(gòu)非常簡(jiǎn)單霹崎,僅包含選擇Single View App Xcode
模板時(shí)創(chuàng)建的文件。 唯一的兩個(gè)major exceptions
是:
-
Assets.xcassets
目錄中的幾個(gè)圖像冶忱。 - 您可以在
Main.storyboard
中找到ViewController
的預(yù)先設(shè)計(jì)的UI尾菇。
現(xiàn)在轉(zhuǎn)到File ? New ? File…
,選擇iOS?CocoaTouch Class
朗和,然后單擊Next
错沽。
在下一個(gè)菜單中,輸入CoolButton
作為類(lèi)名眶拉。 在子類(lèi)字段中千埃,鍵入UIButton
。 對(duì)于語(yǔ)言忆植,請(qǐng)選擇Swift
放可。 現(xiàn)在單擊Next
谒臼,然后單擊Create
。
打開(kāi)CoolButton.swift
并用以下內(nèi)容替換類(lèi)定義:
class CoolButton: UIButton {
var hue: CGFloat {
didSet {
setNeedsDisplay()
}
}
var saturation: CGFloat {
didSet {
setNeedsDisplay()
}
}
var brightness: CGFloat {
didSet {
setNeedsDisplay()
}
}
}
在這里耀里,您將創(chuàng)建三個(gè)屬性蜈缤,用于自定義顏色的色調(diào),飽和度和亮度(hue, saturation and brightness)
冯挎。
設(shè)置屬性后底哥,將觸發(fā)對(duì)setNeedsDisplay
的調(diào)用,以強(qiáng)制UIButton
在用戶(hù)更改顏色時(shí)重繪按鈕房官。
現(xiàn)在趾徽,將以下代碼粘貼到CoolButton
的底部,就在最后一個(gè)花括號(hào)之前:
required init?(coder aDecoder: NSCoder) {
self.hue = 0.5
self.saturation = 0.5
self.brightness = 0.5
super.init(coder: aDecoder)
self.isOpaque = false
self.backgroundColor = .clear
}
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {
return
}
let color = UIColor(hue: hue,
saturation: saturation,
brightness: brightness,
alpha: 1.0)
context.setFillColor(color.cgColor)
context.fill(bounds)
}
在這里翰守,您初始化變量并使用預(yù)先配置的顏色填充按鈕孵奶,以確保一切從一開(kāi)始就正常工作。
Configuring the Button’s UI
打開(kāi)Main.storyboard
以配置UI蜡峰。 有一個(gè)視圖控制器包含三個(gè)滑塊 - 一個(gè)用于色調(diào)了袁,一個(gè)用于飽和度,一個(gè)用于亮度湿颅。
您錯(cuò)過(guò)了UIButton
载绿,因此請(qǐng)將其添加到屏幕頂部。 轉(zhuǎn)到Object Library
肖爵,鍵入UIButton
卢鹦,然后將其拖到屏幕中。
一些自動(dòng)布局的時(shí)間到了劝堪! 按住Ctrl鍵并從按鈕拖動(dòng)到視圖冀自,然后在“安全區(qū)域”中選擇Center Horizontally
。
按住Ctrl鍵并從按鈕拖動(dòng)到hue label
秒啦,然后選擇Vertical Spacing
熬粗。
現(xiàn)在,將按鈕的大小調(diào)整為您喜歡的大小余境。 按住Ctrl鍵并從按鈕向左或向右拖動(dòng)驻呐,然后選擇Width
。
按住control鍵芳来,從按鈕向上或向下拖動(dòng)并選擇Height
含末。
注意:如果通過(guò)拖動(dòng)設(shè)置寬度和高度約束有困難,請(qǐng)選擇按鈕并單擊畫(huà)布右下角的
Add New Contraints
按鈕即舌。 它看起來(lái)有點(diǎn)像星球大戰(zhàn)中的領(lǐng)帶戰(zhàn)士佣盒。
接下來(lái),通過(guò)雙擊并按Delete鍵刪除顯示Button
的文本顽聂。
仍然選中該按鈕肥惭,轉(zhuǎn)到屏幕右側(cè)的Inspectors
側(cè)欄盯仪,然后單擊Identity inspector
。 在Custom Class ? Class
中蜜葱,輸入CoolButton
以使您的按鈕成為CoolButton
類(lèi)的實(shí)例全景。
按鈕就位后,您就可以開(kāi)始將UI連接到代碼了牵囤!
Making Your Button Functional
打開(kāi)ViewController.swift
并使用以下內(nèi)容替換類(lèi)定義:
class ViewController: UIViewController {
@IBOutlet weak var coolButton: CoolButton!
@IBAction func hueValueChanged(_ sender: Any) { }
@IBAction func saturationValueChanged(_ sender: Any) { }
@IBAction func brightnessValueChanged(_ sender: Any) { }
}
在這里爸黄,您將聲明對(duì)剛剛在故事板中創(chuàng)建的按鈕的引用。 您還要聲明配置滑塊的值更改時(shí)發(fā)生的回調(diào)奔浅。
現(xiàn)在馆纳,再次打開(kāi)Main.storyboard
并在頂部欄中選擇Assistant Editor
以并排顯示ViewController.swift
和Main.storyboard
诗良。
按住control鍵從View Controller Scene ? ViewController
將按鈕拖動(dòng)到左側(cè)并將其連接到coolButton outlet
汹桦。
類(lèi)似的,按住control鍵從View Controller Scene ? ViewController
拖出slider鉴裹,并選擇appropriate value-changed
回調(diào)舞骆。
接下來(lái),切換到ViewController.swift
并實(shí)現(xiàn)滑塊的值更改回調(diào):
@IBAction func hueValueChanged(_ sender: Any) {
guard let slider = sender as? UISlider else { return }
coolButton.hue = CGFloat(slider.value)
}
@IBAction func saturationValueChanged(_ sender: Any) {
guard let slider = sender as? UISlider else { return }
coolButton.saturation = CGFloat(slider.value)
}
@IBAction func brightnessValueChanged(_ sender: Any) {
guard let slider = sender as? UISlider else { return }
coolButton.brightness = CGFloat(slider.value)
}
默認(rèn)情況下径荔,UISliders
的范圍為0.0到1.0督禽。 這非常適合您的色調(diào),飽和度和亮度值总处,范圍也從0.0到1.0狈惫,因此您可以直接設(shè)置它們。
經(jīng)過(guò)所有這些工作鹦马,它終于建立并運(yùn)行了胧谈! 如果一切正常,您可以使用滑塊來(lái)填充各種顏色的按鈕:
Drawing Rounded Rectangles
確實(shí)荸频,您可以輕松創(chuàng)建方形按鈕菱肖,但按鈕樣式比芝加哥的天氣變化更快!
事實(shí)上旭从,由于我們最初發(fā)布本教程稳强,方形按鈕和圓角矩形按鈕在按鈕選美中的第一位來(lái)回翻轉(zhuǎn),所以知道如何制作這兩個(gè)版本是個(gè)好主意和悦。
您還可以說(shuō)退疫,通過(guò)簡(jiǎn)單地改變UIView
的corner radius
來(lái)創(chuàng)建圓角矩形非常容易,但那里的樂(lè)趣在哪里鸽素?用這種方式做得更加滿(mǎn)足褒繁,或者說(shuō)是瘋狂。
制作圓角矩形的一種方法是使用CGContextAddArc API
繪制弧付鹿。使用該API澜汤,您可以在每個(gè)角落繪制弧線(xiàn)并繪制線(xiàn)條以連接它們蚜迅。但這很麻煩,需要很多幾何形狀俊抵。
幸運(yùn)的是谁不,有一種更簡(jiǎn)單的方法!你不需要做太多的數(shù)學(xué)運(yùn)算徽诲,它可以很好地繪制圓角矩形刹帕。這是CGContextAddArcToPoint API
。
1. Using the CGContextAddArcToPoint API
CGContextAddArcToPoint API
允許您通過(guò)指定兩條切線(xiàn)和一條半徑來(lái)描述要繪制的弧谎替。 Quartz2D Programming Guide中的下圖顯示了它的工作原理:
當(dāng)您使用矩形時(shí)偷溺,您知道要繪制的每個(gè)弧的切線(xiàn) - 它們只是矩形的邊緣! 您可以根據(jù)矩形的圓角來(lái)指定半徑 - 圓弧越大钱贯,圓角越圓挫掏。
關(guān)于這個(gè)函數(shù)的另一個(gè)好處是,如果路徑中的當(dāng)前點(diǎn)沒(méi)有設(shè)置為告訴弧開(kāi)始繪制的位置秩命,它將從當(dāng)前點(diǎn)到路徑的開(kāi)頭繪制一條線(xiàn)尉共。 因此,您可以將其用作快捷方式弃锐,只需幾次調(diào)用即可繪制圓角矩形袄友。
2. Drawing Your Arcs
由于您要在此Core Graphics
教程中創(chuàng)建一堆圓角矩形,并且希望代碼盡可能可重用霹菊,因此請(qǐng)為所有繪圖方法創(chuàng)建單獨(dú)的文件剧蚣。
轉(zhuǎn)到File ? New ? File…
邢隧,然后選擇iOS ? Swift File
巡通。 按Next
,將其命名為Drawing
卿操,然后單擊Create
柳洋。
現(xiàn)在待诅,使用以下內(nèi)容替換import Foundation
:
import UIKit
import CoreGraphics
extension UIView {
func createRoundedRectPath(for rect: CGRect, radius: CGFloat) -> CGMutablePath {
let path = CGMutablePath()
// 1
let midTopPoint = CGPoint(x: rect.midX, y: rect.minY)
path.move(to: midTopPoint)
// 2
let topRightPoint = CGPoint(x: rect.maxX, y: rect.minY)
let bottomRightPoint = CGPoint(x: rect.maxX, y: rect.maxY)
let bottomLeftPoint = CGPoint(x: rect.minX, y: rect.maxY)
let topLeftPoint = CGPoint(x: rect.minX, y: rect.minY)
// 3
path.addArc(tangent1End: topRightPoint,
tangent2End: bottomRightPoint,
radius: radius)
path.addArc(tangent1End: bottomRightPoint,
tangent2End: bottomLeftPoint,
radius: radius)
path.addArc(tangent1End: bottomLeftPoint,
tangent2End: topLeftPoint,
radius: radius)
path.addArc(tangent1End: topLeftPoint,
tangent2End: topRightPoint,
radius: radius)
// 4
path.closeSubpath()
return path
}
}
上面的代碼為UIView
類(lèi)型的所有內(nèi)容創(chuàng)建了一個(gè)全局?jǐn)U展,因此您不僅可以將它用于UIButton
熊镣。
該代碼還指示createRoundedRectPath(for:radius :)
按以下順序繪制圓角矩形:
以下是代碼中發(fā)生的情況的細(xì)分:
- 1) 您移動(dòng)到頂線(xiàn)部分的中心卑雁。
- 2) 您將每個(gè)角聲明為局部常量。
- 3) 您將每個(gè)弧添加到路徑:
- i) 首先绪囱,在右上角添加一個(gè)圓弧测蹲。 在繪制弧之前,
CGPathAddArcToPoint
將從矩形中間的當(dāng)前位置到弧的開(kāi)始繪制一條線(xiàn)鬼吵。 - ii) 同樣扣甲,您可以為右下角和連接線(xiàn)添加弧。
- iii) 然后為左下角和連接線(xiàn)添加弧。
- iv) 最后琉挖,為左上角和連接線(xiàn)添加弧启泣。
- i) 首先绪囱,在右上角添加一個(gè)圓弧测蹲。 在繪制弧之前,
- 4) 最后,使用
closeSubpath()
連接弧的終點(diǎn)和起點(diǎn)示辈。
3. Making Your Button Rounded
好的寥茫,現(xiàn)在是時(shí)候讓這個(gè)方法起作用了! 打開(kāi)CoolButton.swift
并用以下內(nèi)容替換draw(_ :)
:
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {
return
}
// 1
let outerColor = UIColor(
hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0)
let shadowColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5)
// 2
let outerMargin: CGFloat = 5.0
let outerRect = rect.insetBy(dx: outerMargin, dy: outerMargin)
// 3
let outerPath = createRoundedRectPath(for: outerRect, radius: 6.0)
// 4
if state != .highlighted {
context.saveGState()
context.setFillColor(outerColor.cgColor)
context.setShadow(offset: CGSize(width: 0, height: 2),
blur: 3.0, color: shadowColor.cgColor)
context.addPath(outerPath)
context.fillPath()
context.restoreGState()
}
}
下面進(jìn)行細(xì)分:
- 1) 您定義了兩種顏色矾麻。
- 2) 然后你使用
insetBy(dx:dy :)
得到一個(gè)稍小的矩形(每邊5個(gè)像素)纱耻,你將繪制圓角矩形。 你把它變小了险耀,這樣你就有空間在外面畫(huà)一個(gè)陰影弄喘。 - 3) 接下來(lái),您調(diào)用剛剛編寫(xiě)的函數(shù)
createRoundedRectPath(for:radius :)
甩牺,為圓角矩形創(chuàng)建路徑蘑志。 - 4) 最后,設(shè)置填充顏色和陰影柴灯,添加上下文的路徑卖漫,并調(diào)用
fillPath()
以使用當(dāng)前顏色填充它。
注意:如果您的按鈕當(dāng)前未突出顯示赠群,您只想運(yùn)行代碼;例如旱幼,它沒(méi)有被點(diǎn)擊查描。
構(gòu)建并運(yùn)行應(yīng)用程序;如果一切正常柏卤,您應(yīng)該看到以下內(nèi)容:
Adding a Gradient
好吧冬三,按鈕開(kāi)始看起來(lái)很不錯(cuò),但你可以做得更好缘缚! 添加漸變?cè)趺礃樱?/p>
將以下函數(shù)添加到Drawing.swift
勾笆,以使其普遍適用于任何UIView
:
func drawLinearGradient(
context: CGContext, rect: CGRect, startColor: CGColor, endColor: CGColor) {
// 1
let colorSpace = CGColorSpaceCreateDeviceRGB()
// 2
let colorLocations: [CGFloat] = [0.0, 1.0]
// 3
let colors: CFArray = [startColor, endColor] as CFArray
// 4
let gradient = CGGradient(
colorsSpace: colorSpace, colors: colors, locations: colorLocations)!
// More to come...
}
看起來(lái)并不多,但這個(gè)函數(shù)做了很多桥滨!
- 1) 您需要的第一件事是獲得一個(gè)用于繪制漸變的顏色空間窝爪。
注意:您可以使用色彩空間做很多事情,但99%的時(shí)間您只需要一個(gè)標(biāo)準(zhǔn)的設(shè)備相關(guān)RGB色彩空間齐媒。因此蒲每,只需使用函數(shù)
CGColorSpaceCreateDeviceRGB()
來(lái)獲取所需的引用。
- 2) 接下來(lái)喻括,設(shè)置一個(gè)數(shù)組邀杏,跟蹤漸變范圍內(nèi)每種顏色的位置。值0表示漸變的開(kāi)始唬血,1表示漸變的結(jié)束望蜡。您只有兩種顏色唤崭,并且您希望第一種顏色位于開(kāi)頭,第二種顏色位于結(jié)尾脖律,因此您傳入0和1浩姥。
注意:如果需要,可以在漸變中使用三種或更多顏色状您,并且可以設(shè)置漸變中每種顏色的開(kāi)始位置勒叠。這對(duì)某些效果很有用。
3) 之后膏孟,使用傳遞給函數(shù)的顏色創(chuàng)建一個(gè)數(shù)組眯分。為方便起見(jiàn),您在此處使用普通舊數(shù)組柒桑,但您需要將其轉(zhuǎn)換為
CFArray
弊决,因?yàn)檫@就是API所需要的。4) 然后使用
CGGradient(colorsSpace:colors:locations :)
創(chuàng)建漸變魁淳,傳入顏色空間飘诗,顏色數(shù)組和先前創(chuàng)建的位置。
你現(xiàn)在有一個(gè)漸變引用界逛,但它實(shí)際上還沒(méi)有繪制任何東西昆稿。它只是指向稍后實(shí)際繪制時(shí)使用的信息的指針。
通過(guò)在drawLinearGradient(context:rect:startColor:endColor :)
的末尾添加以下內(nèi)容來(lái)完成該函數(shù):
// 5
let startPoint = CGPoint(x: rect.midX, y: rect.minY)
let endPoint = CGPoint(x: rect.midX, y: rect.maxY)
context.saveGState()
// 6
context.addRect(rect)
// 7
context.clip()
// 8
context.drawLinearGradient(
gradient, start: startPoint, end: endPoint, options: [])
context.restoreGState()
- 5) 您要做的第一件事是計(jì)算要繪制漸變的起點(diǎn)和終點(diǎn)息拜。您只需將其設(shè)置為矩形的
top middle
到bottom middle
的直線(xiàn)溉潭。
其余的代碼可以幫助您在提供的矩形中繪制漸變,關(guān)鍵函數(shù)是drawLinearGradient(_:start:end:options :)
少欺。
1. Constraining a Gradient to a Sub-area
然而喳瓣,關(guān)于該函數(shù)的奇怪之處在于它用漸變填充整個(gè)繪圖區(qū)域 - 沒(méi)有辦法將其設(shè)置為僅用漸變填充子區(qū)域!
好吧赞别,沒(méi)有裁剪(clipping)
畏陕,那就是!
裁剪是Core Graphics
的一個(gè)很棒的功能仿滔,可以將繪圖限制為任意形狀惠毁。您所要做的就是將形狀添加到上下文中,但是不要像通常那樣填充它堤撵,而是調(diào)用clip()
∪侍郑現(xiàn)在,您已將所有未來(lái)的繪圖限制在該區(qū)域实昨!
這就是你在這里做的:
- 6) 您將矩形添加到上下文中洞豁。
- 7) 剪輯到該區(qū)域。
- 8) 然后調(diào)用
drawLinearGradient(_:start:end:options :)
,傳入之前設(shè)置的所有變量丈挟。
那么關(guān)于saveGState()/ restoreGState()
的這些東西究竟是什么呢刁卜?
好吧,Core Graphics
是一個(gè)狀態(tài)機(jī)曙咽。您可以配置所需的一組狀態(tài)蛔趴,例如顏色和線(xiàn)條粗細(xì),然后執(zhí)行操作以實(shí)際繪制它們例朱。這意味著一旦你設(shè)置了某些東西孝情,它就會(huì)一直保持這種狀態(tài),直到你改回來(lái)洒嗤。
好吧箫荡,你剛剛修剪到一個(gè)區(qū)域,所以除非你做了一些事情渔隶,否則你再也無(wú)法在那個(gè)區(qū)域之外畫(huà)畫(huà)了羔挡!
這就是saveGState()/ restoreGState()
可以幫到的地方。
通過(guò)這些间唉,您可以將上下文的當(dāng)前設(shè)置保存到堆棧中绞灼,然后在完成后再將其pop回到原來(lái)的位置。
就是這樣呈野,現(xiàn)在嘗試一下吧低矮!
打開(kāi)CoolButton.swift
并將其添加到draw(_ :)
的底部:
// Outer Path Gradient:
// 1
let outerTop = UIColor(hue: hue, saturation: saturation,
brightness: brightness, alpha: 1.0)
let outerBottom = UIColor(hue: hue, saturation: saturation,
brightness: brightness * 0.8, alpha: 1.0)
// 2
context.saveGState()
context.addPath(outerPath)
context.clip()
drawLinearGradient(context: context, rect: outerRect,
startColor: outerTop.cgColor, endColor: outerBottom.cgColor)
context.restoreGState()
建立并運(yùn)行;你應(yīng)該看到這樣的東西:
- 1) 首先际跪,定義頂部和底部顏色商佛。
- 2) 然后,通過(guò)在堆棧上保存當(dāng)前圖形狀態(tài)姆打,添加路徑,剪切它肠虽,繪制漸變并再次恢復(fù)狀態(tài)來(lái)繪制漸變幔戏。
很好,你的按鈕看起來(lái)很漂亮税课! 一些額外的pizazz
怎么樣闲延?!
Adding a Gloss Effect
現(xiàn)在是時(shí)候讓這個(gè)按鈕閃亮了韩玩,因?yàn)?code>skeuomorphism應(yīng)該永遠(yuǎn)不會(huì)過(guò)時(shí)垒玲!
當(dāng)您為Core Graphics中的按鈕添加光澤(gloss)
效果時(shí),事情會(huì)變得非常復(fù)雜找颓。 如果你感覺(jué)很難合愈,請(qǐng)看看 Matt Gallagher和Michael Heyeck關(guān)于此事的一些偉大作品。
但是對(duì)于我可憐的眼睛,你可以通過(guò)應(yīng)用漸變alpha
蒙版獲得一個(gè)相當(dāng)好看的光澤效果近似佛析,這更容易理解和編碼益老。 所以你要去這么做。
這是你可以全面應(yīng)用于UIView
的東西寸莫,所以將以下函數(shù)添加到Drawing.swift
中的UIView
擴(kuò)展:
func drawGlossAndGradient(
context: CGContext, rect: CGRect, startColor: CGColor, endColor: CGColor) {
// 1
drawLinearGradient(
context: context, rect: rect, startColor: startColor, endColor: endColor)
let glossColor1 = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.35)
let glossColor2 = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.1)
let topHalf = CGRect(origin: rect.origin,
size: CGSize(width: rect.width, height: rect.height/2))
drawLinearGradient(context: context, rect: topHalf,
startColor: glossColor1.cgColor, endColor: glossColor2.cgColor)
}
此函數(shù)基本上是從開(kāi)始到結(jié)束顏色在矩形上繪制漸變捺萌,然后在上半部分添加光澤。 下面進(jìn)行細(xì)分:
- 1) 要繪制漸變膘茎,請(qǐng)調(diào)用之前編寫(xiě)的函數(shù)桃纯。
- 2) 要繪制光澤,然后在其上繪制另一個(gè)漸變披坏,從很透明(白色态坦,0.35 alpha)到非常透明(白色,0.1 alpha)刮萌。
看看它的外觀驮配。 回到CoolButton.swift
并在draw(_ :)
中做一個(gè)小改動(dòng)。 替換此行着茸,這是draw(_ :)
中的倒數(shù)第二行:
drawLinearGradient(context: context, rect: outerRect,
startColor: outerTop.cgColor, endColor: outerBottom.cgColor)
使用
drawGlossAndGradient(context: context, rect: outerRect,
startColor: outerTop.cgColor, endColor: outerBottom.cgColor)
如果您無(wú)法發(fā)現(xiàn)差異壮锻,您只需將drawLinearGradient(context:rect:startColor:endColor :)
更改為drawGlossAndGradient(context:rect:startColor:endColor :)
,即Drawing.swift
中新添加的方法涮阔。
構(gòu)建并運(yùn)行猜绣,您的按鈕現(xiàn)在應(yīng)如下所示:
噢,有光澤敬特!
Styling the Button
現(xiàn)在為超細(xì)掰邢,挑剔的細(xì)節(jié)。 如果您正在制作3D按鈕伟阔,那么您可以全力以赴辣之。 要做到這一點(diǎn),你需要一個(gè)斜角皱炉。
要?jiǎng)?chuàng)建斜角類(lèi)型效果怀估,請(qǐng)?zhí)砑右粭l內(nèi)部路徑,該路徑的漸變略微不同于外部路徑合搅。 將它添加到CoolButton.swift
中的draw(_ :)
底部:
// 1: Inner Colors
let innerTop = UIColor(
hue: hue, saturation: saturation, brightness: brightness * 0.9, alpha: 1.0)
let innerBottom = UIColor(
hue: hue, saturation: saturation, brightness: brightness * 0.7, alpha: 1.0)
// 2: Inner Path
let innerMargin: CGFloat = 3.0
let innerRect = outerRect.insetBy(dx: innerMargin, dy: innerMargin)
let innerPath = createRoundedRectPath(for: innerRect, radius: 6.0)
// 3: Draw Inner Path Gloss and Gradient
context.saveGState()
context.addPath(innerPath)
context.clip()
drawGlossAndGradient(context: context,
rect: innerRect, startColor: innerTop.cgColor, endColor: innerBottom.cgColor)
context.restoreGState()
在這里多搀,您使用insetBy(dx:dy :)
再次縮小矩形,然后獲得一個(gè)圓角矩形并在其上運(yùn)行漸變灾部。 構(gòu)建并運(yùn)行康铭,您將看到一個(gè)微妙的改進(jìn):
Highlighting the Button
你的按鈕看起來(lái)很酷,但它不像一個(gè)按鈕赌髓。 沒(méi)有指示用戶(hù)是否按下了按鈕从藤。
要處理此問(wèn)題催跪,您需要覆蓋觸摸事件以告訴您的按鈕重新顯示自身,因?yàn)樵谟脩?hù)選擇它之后可能需要更新呛哟。
將以下內(nèi)容添加到CoolButton.swift
:
@objc func hesitateUpdate() {
setNeedsDisplay()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
setNeedsDisplay()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
setNeedsDisplay()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
setNeedsDisplay()
perform(#selector(hesitateUpdate), with: nil, afterDelay: 0.1)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
setNeedsDisplay()
perform(#selector(hesitateUpdate), with: nil, afterDelay: 0.1)
}
構(gòu)建并運(yùn)行項(xiàng)目叠荠,當(dāng)你點(diǎn)擊按鈕時(shí),你會(huì)發(fā)現(xiàn)存在差異 - 突出顯示和斜角消失扫责。
但是你可以通過(guò)draw(_:)
來(lái)改善效果:
當(dāng)用戶(hù)按下按鈕時(shí)榛鼎,整個(gè)按鈕應(yīng)該變暗。
您可以通過(guò)為名為actualBrightness
的亮度創(chuàng)建臨時(shí)變量鳖孤,然后根據(jù)按鈕的狀態(tài)適當(dāng)調(diào)整它來(lái)實(shí)現(xiàn)此目的:
var actualBrightness = brightness
if state == .highlighted {
actualBrightness -= 0.1
}
然后者娱,在draw(_ :)
內(nèi),用actualBrightness
替換所有亮度實(shí)例苏揣。
總而言之黄鳍,draw(_ :)
函數(shù)現(xiàn)在看起來(lái)像這樣。 它有點(diǎn)長(zhǎng)平匈,但重復(fù)是值得的:
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {
return
}
var actualBrightness = brightness
if state == .highlighted {
actualBrightness -= 0.1
}
let outerColor = UIColor(
hue: hue, saturation: saturation, brightness: actualBrightness, alpha: 1.0)
let shadowColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5)
let outerMargin: CGFloat = 5.0
let outerRect = rect.insetBy(dx: outerMargin, dy: outerMargin)
let outerPath = createRoundedRectPath(for: outerRect, radius: 6.0)
if state != .highlighted {
context.saveGState()
context.setFillColor(outerColor.cgColor)
context.setShadow(
offset: CGSize(width: 0, height: 2), blur: 3.0, color: shadowColor.cgColor)
context.addPath(outerPath)
context.fillPath()
context.restoreGState()
}
// Outer Path Gloss & Gradient
let outerTop = UIColor(hue: hue, saturation: saturation,
brightness: actualBrightness, alpha: 1.0)
let outerBottom = UIColor(hue: hue, saturation: saturation,
brightness: actualBrightness * 0.8, alpha: 1.0)
context.saveGState()
context.addPath(outerPath)
context.clip()
drawGlossAndGradient(context: context, rect: outerRect,
startColor: outerTop.cgColor, endColor: outerBottom.cgColor)
context.restoreGState()
// Inner Path Gloss & Gradient
let innerTop = UIColor(hue: hue, saturation: saturation,
brightness: actualBrightness * 0.9, alpha: 1.0)
let innerBottom = UIColor(hue: hue, saturation: saturation,
brightness: actualBrightness * 0.7, alpha: 1.0)
let innerMargin: CGFloat = 3.0
let innerRect = outerRect.insetBy(dx: innerMargin, dy: innerMargin)
let innerPath = createRoundedRectPath(for: innerRect, radius: 6.0)
context.saveGState()
context.addPath(innerPath)
context.clip()
drawGlossAndGradient(context: context, rect: innerRect,
startColor: innerTop.cgColor, endColor: innerBottom.cgColor)
context.restoreGState()
}
構(gòu)建并運(yùn)行框沟;點(diǎn)擊它時(shí)按鈕應(yīng)該看起來(lái)很棒!
后記
本篇主要講述了如何制作Glossy效果的按鈕增炭,感興趣的給個(gè)贊或者關(guān)注~~~