移動開發(IOS) – Quartz 2D繪圖
1.Quartz 2D
1.1.Quartz 2D是一個二維圖形繪製引擎,支持iOS環境和Mac OS X環境。
1.2.Quartz 2D API可以實現許多功能,如基於路徑的繪圖、透明度、陰影、顏色管理、反鋸齒、PDF文檔生成和PDF元數據訪問等。
1.3.Quartz 2D API是Core Graphics框架的一部分,因此其中的很多數據類型和方法都是以CG開頭的。會經常見到Quartz 2D(Quartz)和Core Graphics兩個術語交互使用。
1.4.Quartz 2D與分辨率和設備無關,因此在使用Quartz 2D繪圖時,無需考慮最終繪圖的目標設備。
1.5.Quartz中默認的座標系統是:原點(0, 0)在左下角。沿着X軸從左到右座標值逐漸增大;沿着Y軸從下到上座標值逐漸增大。
1.6.座標系的轉換:
CGContextRotateCTM(CGContextRef c, CGFloat angle) | 相對原點旋轉上下文座標系 |
CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty) | 相對原點平移上下文座標系 |
CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy) | 縮放上下文座標系 |
1.6.1.轉換座標系前,使用 CGContextSaveGState(CGContextRef c) 保存當前上下文狀態
1.6.2.座標系轉換後,使用 CGContextRestoreGState(CGContextRef c) 可以恢復之前保存的上下文狀態
1.7.Quartz 2D的繪圖是有順序的,後面畫的可以覆蓋前面畫的。
2.Core Graphics
2.1.Core Graphic 框架是一組基於 C 的 API,當使用 UIKit 創建按鈕、標籤或者其他 UIView 的子類時,UIKit 會用 Core Graphics 將這些元素繪製在屏幕上。此外,UIEvent ( UIKit 中的事件處理類)也會使用 Core Graphics,用來幫助確定觸摸事件在屏幕上所處的位置。
2.2.因爲 UIKit 依賴於 Core Graphics,所以當引入 <UIKit/Uikit.h> 時,Core Graphics 框架會被自動引入,即 UIKit 內部已經引入了 Core Graphics 框架的主頭文件: <CoreGraphics/CoreGraphics.h>。
2.3.UIKit 內部封裝了 Core Graphics 的一些 API,可以快速生成通用的界面元素。
3.Graphics Context
3.1.Graphics Context 是一個數據類型 ( CGContextRef ),封裝了 Quartz 繪製圖像到輸出設備的信息。輸出設備可以是 PDF 文件、 Bitmap 或者顯示器的窗口。
3.2.Quartz 中所有的對象都是繪製到一個 Graphics Context 中。
3.3.當用 Quartz 繪圖時,所有設備相關的特性都包含在 Graphics Context 中。換句話說,我們可以簡單地給Quartz 繪圖序列指定不同的 Graphics Context,就可將相同的圖像繪製到不同的設備上。而不需要任何設備相關的計算,這些都由 Quartz 替我們完成。
3.4.Quartz 提供了以下幾種類型的 Graphics Context:
3.4.1.Bitmap Graphics Context
3.4.2.PDF Graphics Context
3.4.3.Window Graphics Context
3.4.4.Layer Graphics Context
3.4.6.Printer Graphics Context
3.5.一個 Graphics Context 表示一個繪製目標。它包含繪製系統用於完成繪製指令的繪製參數和設備相關信息。
3.6.Graphics Context 定義了基本的繪製屬性,如顏色、裁減區域、線條寬度和樣式信息、字體信息、混合模式等。
3.7.在 iOS 應用程序中,如果要在屏幕上進行繪製,需要創建一個 UIView 對象,並實現它的 drawRect: 方法。視圖的 drawRect: 方法在視圖顯示在屏幕上及它的內容需要更新時被調用。
3.8.在調用自定義的 drawRect: 後,視圖對象自動配置繪圖環境以便能立即執行繪圖操作。
3.9.作爲配置的一部分,視圖對象將爲當前的繪圖環境創建一個 Graphics Context 。通過調用 UIGraphicsGetCurrentContext() 方法可以獲取當前的 Graphics Context。
3.10.UIView 中的 UIGraphicsGetCurrentContext 方法返回的圖形上下文的座標系統的原點位於左上角,而沿着 Y 軸從上到下座標值逐漸增大。於是,在繪圖時無需進行座標轉換。
4.利用 Quartz 2D 繪製 UIView
4.1.當在 UIView 子類中重寫 drawRect: 方法時,iOS 會自動準備好一個圖形上下文,可以通過調用 UIGraphicsGetCurrentContext() 來獲取。
4.2.只要一個 UIView 需要被刷新或者重繪,drawRect: 方法就會被調用,所以 drawRect: 的調用頻率很高。
4.3.重繪時應該調用 setNeedsDisplay ,而不能直接調用 drawRect:, setNeedsDisplay 會自動調用 drawRect: 。
4.4.drawRect: 注意事項:
4.4.1.drawRect: 是在 UIViewController 的 loadView 和 viewDidLoad 兩方法之後調用的。
4.4.2.如果在 UIView 初始化時沒有設置 CGRect,drawRect: 將不會被自動調用。
4.4.3.如果設置 UIView 的 contentMode 屬性值爲 UIViewContentModeRedraw,那麼將在每次更改 frame 時自動調用 drawRect: 。
4.4.4.如果使用 UIView 繪圖,只能在 drawRect: 方法中獲取相應的 CGContextRef 並繪圖。而在其他方法中獲取的 CGContextRef 不能用於繪圖。
5.Quartz內存管理
5.1.使用含有 “Create” 或 “Copy” 的函數創建的對象,使用完後必須釋放,否則將導致內存泄露。使用不含有“Create”或“Copy”的函數獲取的對象,則不需要釋放。
5.2.如果 retain 了一個對象,不再使用時,需要將其 release 掉。可以使用 Quartz 2D 的函數來指定 retain 和 release 一個對象。例如,如果創建了一個 CGColorSpace 對象,則使用函數 CGColorSpaceRetain 和 CGColorSpaceRelease 來 retain 和 release 對象。也可以使用 Core Foundation 的 CFRetain 和 CFRelease 。注意不能傳遞 NULL 值給這些函數。
6.基本繪圖
6.1.繪製直線
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
// 1. 獲取上下文(UIView對應的上下文) CGContextRef context = UIGraphicsGetCurrentContext(); //使用Ref聲明的對象,不需要用* // 2. 創建可變的路徑並設置路徑 (當我們開發動畫的時候,通常需要指定對象運動的路線,然後由動畫方法負責實現動畫效果) CGMutablePathRef path = CGPathCreateMutable(); // 畫線 // 1) 設置起始點 CGPathMoveToPoint(path,
NULL , 50, 50); // 2) 設置目標點 CGPathAddLineToPoint(path,
NULL , 200, 200); CGPathAddLineToPoint(path,
NULL , 50, 200); // 3) 封閉路徑 // a) 直接指定目標點 CGPathAddLineToPoint(path,
NULL , 50, 50); // b) 使用關閉路徑方法 CGPathCloseSubpath(path); // 3. 將路徑添加到上下文 CGContextAddPath(context, path); // 4. 設置上下文屬性 //在使用rgb顏色設置時,最好不要同時指定 rgb 和 alpha,否則會對性能造成一定影響 //默認線條和填充顏色都是黑色 CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); CGContextSetRGBFillColor(context, 0.0, 1.0, 0.0, 1.0); // 設置線條寬度 CGContextSetLineWidth(context, 5.0); // 設置線條的頂點樣式 CGContextSetLineCap(context, kCGLineCapRound); // 設置線條的連接點樣式 CGContextSetLineJoin(context, kCGLineJoinRound); // 設置線條的虛線樣式 /* * 虛線的參數 * context 上下文對象 * phase 相位,虛線起始的位置,通常使用0即可,從頭開始畫虛線 * lengths 長度的數組 * count lengths數組的個數 */ CGFloat lengths[2] = {20.0, 10.0}; CGContextSetLineDash(context, 0.0, lengths, 2); // 5. 繪製路徑 /* * kCGPathStroke: 畫線(空心) * kCGPathFill: 填充(實心) * kCGPathFillStroke: 即畫線又填充 */ CGContextDrawPath(context, kCGPathFillStroke); // 6. 釋放路徑 CGPathRelease(path); |
使用默認的 context 進行繪製直線:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// 1. 獲取上下文 CGContextRef context = UIGraphicsGetCurrentContext(); // 2. 設置當前上下文的路徑 // 1) 設置起始點 CGContextMoveToPoint(context, 50, 50); // 2) 增加點 CGContextAddLineToPoint(context, 200, 200); CGContextAddLineToPoint(context, 50, 200); // 3) 關閉路徑 CGContextClosePath(context); // 3 設置屬性 /* * UIKit默認會導入Core Graphics框架,UIKit對常用的很多CG方法做了封裝 * UIColor setStroke 設置邊線顏色 * UIColor setFill 設置填充顏色 * UIColor set 設置邊線和填充顏色 */ // 設置邊線 // [[UIColor redColor]setStroke]; // 設置填充 // [[UIColor blueColor]setFill]; // 設置邊線和填充 [[UIColor greenColor]set]; // 4 繪製路徑,雖然沒有直接定義路徑,但是第2步操作,就是爲上下文指定路徑 CGContextDrawPath(context, kCGPathFillStroke); |
6.2.繪製矩形
1
2
3
4
5
6
|
CGRect rect = CGRectMake(50, 50, 200.0, 200.0); [[UIColor redColor]set]; // 繪製實心矩形 UIRectFill(rect); // 繪製空心矩形 UIRectFrame(CGRectMake(50, 300, 100, 100)); |
6.3.繪製圓形
1
2
3
4
5
6
7
8
|
// 1. 取出上下文 CGContextRef context = UIGraphicsGetCurrentContext(); CGRect rect = CGRectMake(50, 50, 200, 100); // 2. 設置路徑 UIRectFrame(rect); CGContextAddEllipseInRect(context, rect); // 3. 繪製路徑 CGContextDrawPath(context, kCGPathFillStroke); |
6.4.繪製圓弧
1
2
3
4
5
6
7
8
9
10
11
12
|
// 1. 設置路徑 /* * CGContextAddArc(context, x, y, radius, startAngle, endAngle, clockwise); * context 上下文 * x,y 是圓弧所在圓的中心點座標 * radius 半徑,所在圓的半徑 * startAngle endAngle 起始角度和截止角度 單位是弧度 * clockwise 順時針 0 或者逆時針 1 */ CGContextAddArc(context, 160, 230, 100, -M_PI_4, M_PI_4, 0); // 2. 繪製圓弧 CGContextDrawPath(context, kCGPathFill); |
6.5.繪製文字
1
2
3
4
5
6
7
8
9
10
11
|
NSString
*string = @"Hello world" ; // 獲取字體 ( [UIFont familyNames] ) UIFont *font = [UIFont fontWithName: @"Marker Felt"
size:20]; // 在指定點繪製字符串 [string drawAtPoint:CGPointMake(50, 50) withFont:font]; // 如果在UILabel中,可以將numbersOfLine設置爲0,並且指定足夠的高度即可 CGRect rect = CGRectMake(50, 50, 210, 360); [[UIColor lightGrayColor]set]; UIRectFill(rect); [[UIColor redColor]set]; [string drawInRect:rect withFont:font lineBreakMode: NSLineBreakByWordWrapping
alignment: NSTextAlignmentCenter ]; |
6.6.繪製圖像
1
2
3
4
5
6
7
8
|
UIImage *image = [UIImage imageNamed: @"image.png" ]; // 繪製之後,就無法改變位置,也沒有辦法監聽手勢識別 // 在指定點繪製圖像 [image drawAtPoint:CGPointMake(50, 50)]; // 會在指定的矩形中拉伸繪製 [image drawInRect:CGRectMake(0, 0, 320, 460)]; // 在指定矩形區域中平鋪圖片 [image drawAsPatternInRect:CGRectMake(0, 0, 320, 460)]; |
7.繪製漸變
7.1.徑向漸變
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
// 1. 創建顏色空間 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // 2. 創建漸變 /* * colorSpace 顏色空間 RGB * components 數組,每4個一組,表示一個顏色 {r, g, b, a, r, g, b, a} * locations 表示漸變開始的位置 */ CGFloat components[8] = {1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0}; CGFloat locations[2] = {0.3, 1.0}; CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2); // 漸變的區域剪裁 (整個漸變實際上是完整繪製在屏幕上的,通過裁剪區域,可以讓指定範圍內顯示漸變效果) // 3. 設置裁剪區域範圍 // 4. 繪製漸變 /* * context 上下文 * gradient 漸變 * startCenter 起始中心點 * startRadius 起始半徑,如果指定爲0,就從圓心開始漸變,否則,會空出指定半徑的位置,不填充 * endCenter 截止點(通常和起始中心點重合,即便偏移,也不會太大) * endRadius 截止半徑 * 漸變填充方式 */ CGContextDrawRadialGradient(context, gradient, CGPointMake(160, 230), 10, CGPointMake(0, 0), 150, kCGGradientDrawsAfterEndLocation); // 5. 釋放對象 CGColorSpaceRelease(colorSpace); CGGradientRelease(gradient); |
7.2.線性漸變
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// 1. 創建顏色空間 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // 2. 創建漸變 /* * colorSpace 顏色空間 rgb * components 數組,每4個一組,表示一個顏色 {r, g, b, a, r, g, b, a} * location 表示漸變開始的位置 */ CGFloat components[8] = {1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0}; CGFloat locations[2] = {0.0, 1.0}; CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2); // 漸變的區域剪裁 (整個漸變實際上是完整繪製在屏幕上的,通過裁剪區域,可以讓指定範圍內顯示漸變效果) CGContextClipToRect(context, CGRectMake(0, 360, 200, 100)); // 3. 設置裁剪區域範圍 CGRect rects[5] = {CGRectMake(0, 0, 100, 100), CGRectMake(200, 0, 100, 100), CGRectMake(100, 100, 100, 100), CGRectMake(200, 200, 100, 100), CGRectMake(0, 200, 100, 100)}; CGContextClipToRects(context, rects, 5); // 4. 繪製漸變 CGContextDrawLinearGradient(context, gradient, CGPointMake(0.0, 0.0), CGPointMake(320.0, 460.0), kCGGradientDrawsAfterEndLocation); // 5. 釋放對象 CGColorSpaceRelease(colorSpace); CGGradientRelease(gradient); |
8.生成PDF文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
// 1. 創建PDF上下文 /* * path 保存 PDF 文件的路徑 * bounds 大小如果指定 CGRectZero,則建立612 * 792大小的頁面 * documentInfo 文檔信息 */ NSArray
*array = NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory ,
NSUserDomainMask ,
YES ); NSString
*path = [array[0] stringByAppendingPathComponent: @"test.pdf" ]; UIGraphicsBeginPDFContextToFile(path, CGRectZero,
NULL ); // 2. 創建PDF內容 /* * PDF 中是分頁的,要一個頁面一個頁面的創建 * 使用 UIGraphicsBeginPDFPage 方法可以創建一個 PDF 的頁面 */ for
( NSInteger
i = 0; i < 6; i++) { // 1) 創建PDF頁面,每個頁面的裝載量是有限的 if
(i % 2 == 0) { UIGraphicsBeginPDFPage(); } // 2) 將Image添加到PDF文件 (一個頁面裝2張圖片) NSString
*fileName = [ NSString
stringWithFormat: @"NatGeo%02d.png" , i + 1]; UIImage *image = [UIImage imageNamed:fileName]; [image drawInRect:CGRectMake(0, (i % 2) * 396, 612, 396)]; } // 3. 關閉PDF上下文 UIGraphicsEndPDFContext(); |
9.Quantz 2D 基本繪圖方法
函數 | 方法 |
CGContextBeginPath | 開始一個新路徑 |
CGContextMoveToPoint | 設置路徑的起點 |
CGContextClosePath | 關閉路徑 |
CGContextAddPath | 添加路徑 |
CGContextAddLineToPoint | 在指定點添加線 |
CGContextAddLines | 添加多條線 |
CGContextAddRect | 添加矩形 |
CGContextAddRects | 添加多個矩形 |
CGContextAddEllipseInRect | 在矩形區域中添加橢圓 |
CGContextAddArc | 添加圓弧 |
CGContextAddArcToPoint | 在指定點添加圓弧 |
CGContextAddCurveToPoint | 在指定點添加曲線 |
CGContextDrawPath | 繪製路徑 |
CGContextFillPath | 實心路徑 |
CGContextFillRect | 實心矩形 |
CGContextFillRects | 多個實心矩形 |
CGContextFillEllipseInRect | 在矩形區域中繪製實心橢圓 |
CGContextStrokePath | 空心路徑 |
CGContextStrokeRect | 空心矩形 |
CGContextStrokeRectWithWidth | 使用寬度繪製空心矩形 |
CGContextStrokeEllipseInRect | 在矩形區域中繪製空心橢圓 |
CGContextSetLineWidth | 設置線寬 |
CGContextSetBlendMode | 設置混合模式 |
CGContextSetShouldAntialias | 設置抗鋸齒效果 |
CGContextSetLineCap | 設置線條收尾點樣式 |
CGContextSetLineJoin | 設置線條連接點樣式 |
CGContextSetLineDash | 設置虛線 |