(一)什么是Quartz 2D?
- Quartz 2D是一個(gè)二維繪圖引擎葡秒,支持iOS和Mac OS系統(tǒng)。
(二)Quartz 2D可以做的事情?
- 繪制圖形:線條眯牧、三角形蹋岩、矩形、圓形学少、弧形等
- 手勢(shì)解鎖
- 繪制圖表星澳,折線圖、柱狀圖旱易、餅狀圖
- 繪制文字
- 涂鴉
- 繪制和生成圖片
- 涂鴉
- 讀取和生成PDF
- 截圖和裁剪
- 頭像裁剪
- 自定義UI控件
(三)Quartz 2D在iOS開(kāi)發(fā)中的價(jià)值
在開(kāi)發(fā)iOS程序時(shí),iOS提供了UIKit框架讓我們可以利用UIKit框架中的各種各樣的UI控件去搭建美觀的UI界面腿堤。比如:UILabel阀坏、UIImageView、UIButton笆檀。利用這些UI控件我們可以實(shí)現(xiàn)一些常見(jiàn)的界面忌堂。但是有些時(shí)候,有些UI界面極其復(fù)雜酗洒,用普通的UI控件無(wú)法實(shí)現(xiàn)士修,這是可以利用Quartz 2D技術(shù)畫(huà)出我們需要的復(fù)雜結(jié)構(gòu)的UI控件。
所以Quartz 2D的最大的價(jià)值就是自定義實(shí)現(xiàn)我們需要的但是系統(tǒng)沒(méi)有提供的UI控件
(四)圖形上下文(理解成畫(huà)板)
在Quartz 2D中的重要概念之一就是圖形上下文樱衷,我們可以理解成畫(huà)畫(huà)的畫(huà)板
圖形上下文是CGContextRef類(lèi)型
(五)圖形上下文的作用
- 保存繪圖信息棋嘲、繪圖狀態(tài)
- 決定繪制的輸出目標(biāo)(繪制到什么地方去?)
- 輸出目標(biāo)可以是PDF文件矩桂、Bitmap沸移、顯示器的窗口
- 相同的繪圖序列,指定不同的Graphics Context侄榴,就可將相同的圖像繪制到不同的目標(biāo)上
(六)Graphics Context的類(lèi)型
- Bitmap Graphics Context
- PDF Graphics Context
- Window Graphics Context
- Layer Graphics Context
- Printer Graphics Context
(七)自定義UIView
利用Quartz 2D繪制東西到UIView上需要兩個(gè)前提條件:
- 需要一個(gè)圖形上下文雹锣。(保存繪制信息、決定繪到什么地方去)
- 該圖形上下文跟
UIView
關(guān)聯(lián)癞蚕,如此才能將內(nèi)容繪制到UIIView
上去
實(shí)現(xiàn)步驟:
- 自定義
View
繼承自UIView
- 實(shí)現(xiàn)系統(tǒng)的
drawRect
方法 - 在
drawRect
方法中
3.1. 獲取跟當(dāng)前View
相關(guān)聯(lián)的圖形上下文
3.2. 繪制相應(yīng)的圖形內(nèi)容
3.3. 利用圖形上下文將繪制的所有內(nèi)容渲染顯示到View
上面
(八)drawRect
為什么要實(shí)現(xiàn)drawRect
方法才能繪圖到View
上蕊爵?
- 因?yàn)樵?code>drawRect方法中才能取得跟
View
相關(guān)聯(lián)的圖形上下文 - 我們需要通過(guò)在上下文中繪制內(nèi)容,然后把上下文中的內(nèi)容渲染到
view
的layer
上
drawRect
方法在什么時(shí)候調(diào)用桦山?
- 當(dāng)
view
第一次顯示到屏幕上時(shí)(被添加到UIWindow
上時(shí)調(diào)用) - 調(diào)用
view
的setNeedsDisplay
或者setNeedsDisplayRect
時(shí)調(diào)用
(九)Quartz 2D繪圖代碼的步驟
1. 獲得圖形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
2. 拼接路徑(下面代碼是搞一條線段)
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);
3. 繪制路徑
CGContextStrokePath(ctx); // CGContextFillPath(ctx);
(十)常用拼接路徑的函數(shù)
新建一個(gè)起點(diǎn)
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加新的線段到某個(gè)點(diǎn)
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加一個(gè)矩形
void CGContextAddRect(CGContextRef c, CGRect rect)
添加一個(gè)橢圓
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
添加一個(gè)圓弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
(十一)常用繪制路徑的函數(shù)
Mode參數(shù)決定繪制的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
繪制空心路徑
void CGContextStrokePath(CGContextRef c)
繪制實(shí)心路徑
void CGContextFillPath(CGContextRef c)
提示:一般以CGContextDraw攒射、CGContextStroke、CGContextFill開(kāi)頭的函數(shù)恒水,都是用來(lái)繪制路徑的
(十二)上下文狀態(tài)棧
上下文狀態(tài)棧是上下文用來(lái)存儲(chǔ)路徑狀態(tài)的匆篓,這里我們通過(guò)一個(gè)例子來(lái)說(shuō)明上下文狀態(tài)棧的原理
在view上畫(huà)一個(gè)“十”字,一橫為紅色寇窑,寬度為10鸦概,一豎為綠色,寬度20 ???
錯(cuò)誤示例:
let context = UIGraphicsGetCurrentContext()
let path = UIBezierPath()
path.move(to: CGPoint(x: 20, y: 100))
path.addLine(to: CGPoint(x: 180, y: 100))
context?.addPath(path.cgPath)
context?.setStrokeColor(UIColor.red.cgColor)
context?.setLineWidth(10)
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 100, y: 20))
path1.addLine(to: CGPoint(x: 100, y: 180))
context?.addPath(path1.cgPath)
context?.setStrokeColor(UIColor.green.cgColor)
context?.setLineWidth(20)
context?.strokePath()
首先分析一下為什么上面這段代碼無(wú)法呈現(xiàn)出我們想要的效果
上面的代碼首先獲取和當(dāng)前view相關(guān)聯(lián)的上下文
let context = UIGraphicsGetCurrentContext()
然后,畫(huà)兩條需要的線窗市,且分別添加到上下文上
let path = UIBezierPath()
path.move(to: CGPoint(x: 20, y: 100))
path.addLine(to: CGPoint(x: 180, y: 100))
context?.addPath(path.cgPath)
context?.setStrokeColor(UIColor.red.cgColor)
context?.setLineWidth(10)
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 100, y: 20))
path1.addLine(to: CGPoint(x: 100, y: 180))
context?.addPath(path1.cgPath)
context?.setStrokeColor(UIColor.green.cgColor)
context?.setLineWidth(20)
然后再把畫(huà)好的兩條線渲染到和當(dāng)前上下文關(guān)聯(lián)的view的layer上
context?.strokePath()
這里呈現(xiàn)的效果是兩條顏色寬度都為綠色的線
為什么是兩條綠色的線先慷?是因?yàn)樯舷挛牡臓顟B(tài)會(huì)被覆蓋,第二次設(shè)置的狀態(tài)會(huì)覆蓋第一次設(shè)置的狀態(tài)咨察,導(dǎo)致上下文中所有的線都是最后一次設(shè)置的路徑狀態(tài)(寬度為20论熙,顏色為綠色)
為了防止覆蓋的問(wèn)題,我們可以先畫(huà)好一根線摄狱,再畫(huà)第二根線
// 1. 獲取當(dāng)前view相關(guān)聯(lián)的上下文
/*
* 獲取上下文相當(dāng)于開(kāi)辟了一塊內(nèi)存空間
* 該內(nèi)存空間分為兩部分
* 1. 存儲(chǔ)路徑
* 2. 存儲(chǔ)路徑的狀態(tài)(UIColor脓诡,Width)
*/
let context = UIGraphicsGetCurrentContext()
let path = UIBezierPath()
path.move(to: CGPoint(x: 100, y: 20))
path.addLine(to: CGPoint(x: 100, y: 180))
context?.addPath(path.cgPath)
context?.setStrokeColor(UIColor.red.cgColor)
context?.setLineWidth(10)
// 取出上下文中所有繪制的路徑,并把上下文中存儲(chǔ)的路徑狀態(tài)應(yīng)用到所有的路徑上媒役,渲染到上下文關(guān)聯(lián)的view的layer上
context?.strokePath()
/************************分割線************************/
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 20, y: 100))
path1.addLine(to: CGPoint(x: 180, y: 100))
context?.addPath(path1.cgPath)
context?.setStrokeColor(UIColor.green.cgColor)
context?.setLineWidth(20)
context?.strokePath()
通過(guò)上下文狀態(tài)棧來(lái)實(shí)現(xiàn)類(lèi)似效果
首先如果我們想要在一個(gè)view上通過(guò)Quartz 2D技術(shù)畫(huà)路徑(圖)祝谚,我們需要獲取和當(dāng)前view相關(guān)聯(lián)的上下文(context)
獲取上下文相當(dāng)于在內(nèi)存中開(kāi)辟了一塊存儲(chǔ)空間。
該內(nèi)存空間分為兩部分酣衷,這里用A交惯、B表示:
A部分. 存儲(chǔ)繪制的路徑
B部分. 存儲(chǔ)路徑的狀態(tài)(路徑的顏色、路徑的寬度)
let context = UIGraphicsGetCurrentContext()
然后繪制路徑穿仪,并添加到上下文中席爽。這部分路徑會(huì)存儲(chǔ)在A部分
let path = UIBezierPath()
path.move(to: CGPoint(x: 20, y: 100))
path.addLine(to: CGPoint(x: 180, y: 100))
context?.addPath(path.cgPath)
再設(shè)置上下文中第二條路徑(豎)的狀態(tài)(顏色紅,寬度為10)啊片。這部分狀態(tài)會(huì)存儲(chǔ)在B部分
context?.setStrokeColor(UIColor.red.cgColor)
context?.setLineWidth(10)
我們先保存B部分的狀態(tài)到上下文狀態(tài)棧中
/*
* 保存上下文中存儲(chǔ)的狀態(tài)到上下文狀態(tài)棧中(棧:先進(jìn)后出)
* 保存了:color:red只锻,width:10到上下文狀態(tài)棧中
*/
context?.saveGState()
然后再設(shè)置第一條路徑的狀態(tài)(顏色綠,寬度20)紫谷,并渲染到當(dāng)前上下文關(guān)聯(lián)的view
的layer
上
context?.setStrokeColor(UIColor.green.cgColor)
context?.setLineWidth(20)
context?.strokePath()
再繪制第二條路徑炬藤,并取出上下文狀態(tài)棧中保存的第一條路徑的狀態(tài),并渲染到當(dāng)前上下文關(guān)聯(lián)的view
的layer
上
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 100, y: 20))
path1.addLine(to: CGPoint(x: 100, y: 180))
context?.addPath(path1.cgPath)
/*
* 從當(dāng)前上下文的狀態(tài)棧中碴里,取出棧頂?shù)模ㄗ詈蟠嫒氲臓顟B(tài))狀態(tài)沈矿,覆蓋上下文中存儲(chǔ)的狀態(tài)
*/
context?.restoreGState()
context?.strokePath()
(十三)上下文的矩陣操作
let context = UIGraphicsGetCurrentContext()
let path = UIBezierPath(ovalIn: CGRect(x: 100, y: 100, width: 200, height: 130))
// 矩陣操作:平移
context?.translateBy(x: 0, y: 200)
// 矩陣操作:縮放
context?.scaleBy(x: 0.8, y: 1.3)
// 矩陣操作:旋轉(zhuǎn)
context?.rotate(by: .pi/6)
// 注意:矩陣操作要在路徑添加前進(jìn)行,否則對(duì)所添加的路徑無(wú)效
context?.addPath(path.cgPath)
context?.strokePath()