什么是Quartz2D?
Quartz 2D是一個(gè)二維繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng)
- Quartz 2D能完成的工作
- 繪制圖形 : 線條\三角形\矩形\圓\弧等
- 繪制文字
- 繪制\生成圖片(圖像)
- 讀取\生成PDF
- 截圖\裁剪圖片
- 自定義UI控件
Quartz2D在iOS開發(fā)中的價(jià)值:
- 繪制一些系統(tǒng)UIKit框架中不好展示的內(nèi)容阐枣,例如餅圖
- 自定義一些控件
- 不添加UI控件的情況下涛漂,使UI內(nèi)容更豐富
- ……
iOS中,大部分控件都是Quartz2D繪制出來的
圖形上下文
圖形上下文就相當(dāng)于畫布氢橙,不同類型的畫布就是決定著畫得內(nèi)容將展示在哪里酝枢。
- Quartz2D提供了以下幾種類型的Graphics Context:
- Bitmap Graphics Context 位圖上下文,在這個(gè)上下文上繪制或者渲染的內(nèi)容悍手,可以獲取成圖片(需要主動(dòng)創(chuàng)建一個(gè)位圖上下文來使用帘睦,使用完畢袍患,一定要銷毀)
- PDF Graphics Context
- Window Graphics Context
- Layer Graphics Context 圖層上下文,針對(duì)UI控件的上下文
- Printer Graphics Context
drawRect:
為什么要實(shí)現(xiàn)drawRect:方法才能繪圖到view上竣付?
因?yàn)樵赿rawRect:方法中才能取得跟view相關(guān)聯(lián)的圖形上下文
drawRect:中取得的上下文
在drawRect:方法中取得上下文后诡延,就可以繪制東西到view上
View內(nèi)部有個(gè)layer(圖層)屬性,drawRect:方法中取得的是一個(gè)Layer Graphics Context古胆,因此肆良,繪制的東西其實(shí)是繪制到view的layer上去了
View之所以能顯示東西,完全是因?yàn)樗鼉?nèi)部的layer
drawRect:方法的調(diào)用逸绎?
- 當(dāng)view第一次顯示到屏幕上時(shí)惹恃,系統(tǒng)會(huì)創(chuàng)建好一個(gè)跟當(dāng)前view相關(guān)的Layer上下文
- 系統(tǒng)會(huì)通過此上下文,在drawRect:方法中繪制好當(dāng)前view的內(nèi)容
- 主動(dòng)讓view重繪內(nèi)容的時(shí)候棺牧,調(diào)用setNeedsDisplay或者setNeedsDisplayInRect:巫糙。我們主動(dòng)調(diào)用drawRect:方法是無效的。
- 調(diào)用view的setNeedsDisplay或者setNeedsDisplayInRect:時(shí)颊乘。
- 注意:setNeedsDisplay和setNeedsDisplayInRect:方法調(diào)用后参淹,屏幕并不是立即刷新,而是會(huì)在下一次刷新屏幕的時(shí)候把繪制的內(nèi)容顯示出來疲牵。
也正是系統(tǒng)會(huì)在調(diào)用這個(gè)方法之前創(chuàng)建一個(gè)與該view相關(guān)的上下文承二,才讓我們可以在drawRect:方法中繪制。注意:在其他地方拿不到view相關(guān)的上下文纲爸,所以不能實(shí)現(xiàn)繪制亥鸠。
自定義view
如何利用Quartz2D繪制東西到view上?
- 首先识啦,得有圖形上下文负蚊,因?yàn)樗鼙4胬L圖信息,并且決定著繪制到什么地方去
- 其次颓哮,那個(gè)圖形上下文必須跟view相關(guān)聯(lián)家妆,才能將內(nèi)容繪制到view上面
自定義view的步驟:
- 新建一個(gè)類,繼承自UIView
- 實(shí)現(xiàn)- (void)drawRect:(CGRect)rect方法冕茅,然后在這個(gè)方法中
- 取得跟當(dāng)前view相關(guān)聯(lián)的圖形上下文
- 繪制相應(yīng)的圖形內(nèi)容
- 利用圖形上下文將繪制的所有內(nèi)容渲染顯示到view上面
常用拼接路徑函數(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開頭的函數(shù)姨伤,都是用來繪制路徑的
圖形上下文棧的操作
將當(dāng)前的上下文copy一份,保存到棧頂(那個(gè)棧叫做”圖形上下文椛谄海”)
void CGContextSaveGState(CGContextRef c)
將棧頂?shù)纳舷挛某鰲?替換掉當(dāng)前的上下文
void CGContextRestoreGState(CGContextRef c)
矩陣操作
利用矩陣操作,能讓繪制到上下文中的所有路徑一起發(fā)生變化
縮放
void CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
旋轉(zhuǎn)
void CGContextRotateCTM(CGContextRef c, CGFloat angle)
平移
void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
繪圖的核心步驟:
- 獲得上下文
- 繪制/拼接繪圖路徑
- 將路徑添加到上下文
- 渲染上下文
記渍С:所有的繪圖当编,都是這個(gè)步驟,即使使用貝塞爾路徑徒溪,也只是對(duì)這個(gè)步驟進(jìn)行了封裝忿偷。對(duì)于繪圖而言金顿,拿到上下文很關(guān)鍵。
貝塞爾路徑
就是UIKit框架中鲤桥,對(duì)繪圖的封裝揍拆。實(shí)際操作起來,使用貝塞爾路徑芜壁,更為方便礁凡。
- 用法與CGContextRef類似,但是oc對(duì)其進(jìn)行了封裝慧妄,更加面向?qū)ο蟆?/li>
- 常用的方法:
返回一個(gè)描述橢圓的路徑:
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect
;設(shè)置起始點(diǎn):
- (void)moveToPoint:(CGPoint)point
;添加直線到一點(diǎn):
- (void)addLineToPoint:(CGPoint)point
;三次貝塞爾曲線:
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
;
-
貝塞爾曲線:
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint
;
-
繪制圓弧:
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
;
封閉閉路徑:
- (void)closePath
;
裁剪核心代碼
// 開啟一個(gè)位圖(圖片)上下文
//size:上下文尺寸
//opaque:不透明顷牌。一般是透明的,所以設(shè)置為NO
//scale:縮放塞淹,如果不縮放窟蓝,設(shè)置為0就好
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 描述圓形的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 把圓形路徑設(shè)置裁剪區(qū)域(將區(qū)域外的內(nèi)容裁剪掉,是現(xiàn)實(shí)區(qū)域內(nèi)的內(nèi)容)
[path addClip];
// 繪制圖片(先設(shè)置裁剪區(qū)域饱普,再裁剪运挫,才會(huì)有效果)
[image drawAtPoint:CGPointZero];
// 從上下文中內(nèi)容生成一張圖片
image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉上下文(一定不要忘了關(guān)閉自己開啟的上下文)
UIGraphicsEndImageContext();
截屏核心代碼
// 開啟一個(gè)跟屏幕一樣大的尺寸的上下文
UIGraphicsBeginImageContextWithOptions(caputeView.bounds.size, NO, 0);
// 獲取自己創(chuàng)建的位圖上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// view之所以你能顯示內(nèi)容,是因?yàn)橛袌D層套耕,因此只要把圖層畫到上下文
// 圖層只能渲染谁帕,不能繪制
[caputeView.layer renderInContext:ctx];
// 從上下文中生成一張新的圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉上下文
UIGraphicsEndImageContext();