一、 Quartz2D概述及做用app
Quartz2D的API是純C語言的,Quartz2D的API來自於Core Graphics框架。框架
數據類型和函數基本都以CG做爲前綴,好比:函數
CGContextRefatom
CGPathRef對象
CGContextStrokePath(ctx);繼承
……遊戲
Quartz 2D是一個二維繪圖引擎,同時支持iOS和Mac系統。圖片
Quartz 2D能完成的工做:ip
繪製圖形 : 線條\三角形\矩形\圓\弧等;內存
繪製文字;
繪製\生成圖片(圖像);
讀取\生成PDF;
截圖\裁剪圖片;
自定義UI控件;
… …
2、Quartz2D在iOS開發中的價值
爲了便於搭建美觀的UI界面,iOS提供了UIKit框架,裏面有各類各樣的UI控件,好比:
UILabel:顯示文字;
UIImageView:顯示圖片;
UIButton:同時顯示圖片和文字(能點擊);
… …
利用UIKit框架提供的控件,拼拼湊湊,能搭建和現實一些簡單、常見的UI界面。
可是,有些UI界面極其複雜、並且比較個性化,用普通的UI控件沒法實現,這時能夠利用Quartz2D技術將控件內部的結構畫出來,自定義控件的樣子。其實,iOS中大部分控件的內容都是經過Quartz2D畫出來的。所以,Quartz2D在iOS開發中很重要的一個價值是:自定義view(自定義UI控件)。
3、圖形上下文
圖形上下文(Graphics Context):是一個CGContextRef類型的數據。
圖形上下文的做用:
(1)保存繪圖信息、繪圖狀態
(2)決定繪製的輸出目標(繪製到什麼地方去?)
(輸出目標能夠是PDF文件、Bitmap或者顯示器的窗口上)
相同的一套繪圖序列,指定不一樣的Graphics Context,就可將相同的圖像繪製到不一樣的目標上。
Quartz2D提供瞭如下幾種類型的Graphics Context:
(1)Bitmap Graphics Context
(2)PDF Graphics Context
(3)Window Graphics Context
(4)Layer Graphics Context
(5)Printer Graphics Context
4、自定義view
如何利用Quartz2D自定義view?(自定義UI控件)如何利用Quartz2D繪製東西到view上?
首先,得有圖形上下文,由於它能保存繪圖信息,而且決定着繪製到什麼地方去。
其次,那個圖形上下文必須跟view相關聯,才能將內容繪製到view上面。
自定義view的步驟:
(1)新建一個類,繼承自UIView
(2)實現- (void)drawRect:(CGRect)rect方法,而後在這個方法中
(a)取得跟當前view相關聯的圖形上下文
例如:CGContextRef ctx = UIGraphicsGetCurrentContext();
(a) 繪製相應的圖形內容
例如:畫1/4圓
CGContextMoveToPoint(ctx, 100, 100);
CGContextAddLineToPoint(ctx, 100, 150);
CGContextAddArc(ctx, 100, 100, 50, -M_PI_2, M_PI, 1);
CGContextClosePath(ctx);
[[UIColor redColor] set];
(b)利用圖形上下文將繪製的全部內容渲染顯示到view上面
例如:CGContextFillPath(ctx);
5、drawRect
爲何要實現drawRect:方法才能繪圖到view上?
由於在drawRect:方法中才能取得跟view相關聯的圖形上下文。
drawRect:方法在何時被調用?
(1)當view第一次顯示到屏幕上時(被加到UIWindow上顯示出來)。
(2)調用view的setNeedsDisplay或者setNeedsDisplayInRect:時。
setNeedsDisplay常被調用來刷新View界面。
繪圖順序:
三、 drawRect:中取得的上下文
在drawRect:方法中取得上下文後,就能夠繪製東西到view上。View內部有個layer(圖層)屬性,drawRect:方法中取得的是一個Layer Graphics Context,所以,繪製的東西實際上是繪製到view的layer上去了。View之因此能顯示東西,徹底是由於它內部的layer。
四、 Quartz2D繪圖的代碼步驟
第一步:得到圖形上下文:
CGContextRef ctx = UIGraphicsGetCurrentContext();
第二步:拼接路徑(下面代碼是搞一條線段):
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);
第三步:繪製路徑:
CGContextStrokePath(ctx); // CGContextFillPath(ctx);
7、經常使用拼接路徑函數
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
void CGContextAddRect(CGContextRef c, CGRect rect)
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
8、經常使用繪製路徑函數
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
void CGContextStrokePath(CGContextRef c)
void CGContextFillPath(CGContextRef c)
提示:通常以CGContextDraw、CGContextStroke、CGContextFill開頭的函數,都是用來繪製路徑的
其餘經常使用函數:
設置線段寬度:
CGContextSetLineWidth(ctx, 10);
設置線段頭尾部的樣式:
CGContextSetLineCap(ctx, kCGLineCapRound);
設置線段轉折點的樣式:
CGContextSetLineJoin(ctx, kCGLineJoinRound);
設置顏色:
CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
9、圖形上下文棧的操做
將當前的上下文copy一份,保存到棧頂(那個棧叫作」圖形上下文棧」):
void CGContextSaveGState(CGContextRef c)
將棧頂的上下文出棧,替換掉當前的上下文(清空以前對於上下文設置):
void CGContextRestoreGState(CGContextRef c)
10、矩陣操做
利用矩陣操做,能讓繪製到上下文中的全部路徑一塊兒發生變化:
縮放:
void CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
旋轉:
void CGContextRotateCTM(CGContextRef c, CGFloat angle)
平移:
void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
11、Quartz2D的內存管理
關於內存管理,有如下原則:
(1)使用含有「Create」或「Copy」的函數建立的對象,使用完後必須釋放,不然將致使內存泄露。
(2)使用不含有「Create」或「Copy」的函數獲取的對象,則不須要釋放
(3)若是retain了一個對象,再也不使用時,須要將其release掉。
(4)可使用Quartz 2D的函數來指定retain和release一個對象。例如,若是建立了一個CGColorSpace對象,則使用函數CGColorSpaceRetain和CGColorSpaceRelease來retain和release對象。
(5)也可使用Core Foundation的CFRetain和CFRelease。注意不能傳遞NULL值給這些函數。
十一、 圖片水印
有時候,在手機客戶端app中也須要用到水印技術,好比,用戶拍完照片後,能夠在照片上打個水印,標識這個圖片是屬於哪一個用戶的
實現方式:
利用Quartz2D,將水印(文字、LOGO)畫到圖片的右下角
核心代碼:
開啓一個基於位圖的圖形上下文:
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
從上下文中取得圖片(UIImage):
UIImage* UIGraphicsGetImageFromCurrentImageContext();
結束基於位圖的圖形上下文:
void UIGraphicsEndImageContext();
例如:
UIImage *bgImage = [UIImage imageNamed:@"scene"];
// 上下文 : 基於位圖(bitmap) , 全部的東西須要繪製到一張新的圖片上去
// 1.建立一個基於位圖的上下文(開啓一個基於位圖的上下文)
// size : 新圖片的尺寸
// opaque : YES : 不透明, NO : 透明
// 這行代碼事後.就至關於常見一張新的bitmap,也就是新的UIImage對象
UIGraphicsBeginImageContextWithOptions(bgImage.size, NO, 0.0);
// 2.畫背景
//特別注意的是使用OC自帶的畫圖方法不用傳上下文,系統會自動檢測並傳入上下文
[bgImage drawInRect:CGRectMake(0, 0, bgImage.size.width, bgImage.size.height)];
// 3.畫右下角的水印
UIImage *waterImage = [UIImage imageNamed:@"logo"];
CGFloat scale = 0.2;
CGFloat margin = 5;
CGFloat waterW = waterImage.size.width * scale;
CGFloat waterH = waterImage.size.height * scale;
CGFloat waterX = bgImage.size.width - waterW - margin;
CGFloat waterY = bgImage.size.height - waterH - margin;
[waterImage drawInRect:CGRectMake(waterX, waterY, waterW, waterH)];
// 4.從上下文中取得製做完畢的UIImage對象
UIImage *newImage =
UIGraphicsGetImageFromCurrentImageContext();
// 5.結束上下文
UIGraphicsEndImageContext();
// 6.顯示到UIImageView
self.iconView.image = newImage;
// 7.將image對象壓縮爲PNG格式的二進制數據
NSData *data = UIImagePNGRepresentation(newImage);
//UIImageJPEGRepresentation(<#UIImage *image#>, <#CGFloat compressionQuality#>)
// 8.寫入文件
NSString *path =
[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]
stringByAppendingPathComponent: @"new.png"];
[data writeToFile:path atomically:YES];
13、圖片裁剪
有時候咱們須要把一張普通的圖片刻意裁剪成圓形,好比把用戶頭像作成圓形的,以下圖:
核心代碼:
void CGContextClip(CGContextRef c)
將當前上下所繪製的路徑裁剪出來(超出這個裁剪區域的都不能顯示)
示例代碼1(效果如上圖):
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 畫圓
CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 50, 50));
// 裁剪
CGContextClip(ctx);
CGContextFillPath(ctx);
// 顯示圖片
UIImage *image = [UIImage imageNamed:@"me"];
[image drawAtPoint:CGPointMake(100, 100)];
示例代碼2(產生圖像圓截圖,效果和上面圖片效果同樣但在圖像周圍加個圓邊框,並保存到本地):
// 1.加載原圖
UIImage *oldImage = [UIImage imageNamed:@"me"];
// 2.開啓上下文
CGFloat borderW = 2; // 圓環的寬度
CGFloat imageW = oldImage.size.width + 2 * borderW;
CGFloat imageH = oldImage.size.height + 2 * borderW;
CGSize imageSize = CGSizeMake(imageW, imageH);
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
// 3.取得當前的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 4.畫邊框(大圓)
[[UIColor whiteColor] set];
CGFloat bigRadius = imageW * 0.5; // 大圓半徑
CGFloat centerX = bigRadius; // 圓心
CGFloat centerY = bigRadius;
CGContextAddArc(ctx, centerX, centerY, bigRadius, 0, M_PI * 2, 0);
CGContextFillPath(ctx); // 畫圓
// 5.小圓
CGFloat smallRadius = bigRadius - borderW;
CGContextAddArc(ctx, centerX, centerY, smallRadius, 0, M_PI * 2, 0);
// 裁剪(後面畫的東西纔會受裁剪的影響)
CGContextClip(ctx);
// 6.畫圖
[oldImage drawInRect:CGRectMake(borderW, borderW, oldImage.size.width, oldImage.size.height)];
// 7.取圖
UIImage *newImage =
UIGraphicsGetImageFromCurrentImageContext();
// 8.結束上下文
UIGraphicsEndImageContext();
// 9.顯示圖片
self.iconView.image = newImage;
// 10.寫出文件
NSData *data = UIImagePNGRepresentation(newImage);
NSString *path =
[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) lastObject]
stringByAppendingPathComponent:@"new.png"];
[data writeToFile:path atomically:YES];
14、屏幕截圖
有時候須要截取屏幕上的某一塊內容,好比捕魚達人遊戲:
核心代碼:
- (void)renderInContext:(CGContextRef)ctx;
調用某個view的layer的renderInContext:方法便可
示例代碼:
// 1.開啓上下文
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
// 2.將控制器view的layer渲染到上下文
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
// 3.取出圖片
UIImage *newImage =
UIGraphicsGetImageFromCurrentImageContext();
// 4.寫文件
NSData *data = UIImagePNGRepresentation(newImage);
NSString *path =
[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) lastObject]
stringByAppendingPathComponent:@"new.png"];
[data writeToFile:path atomically:YES];
// 5.結束上下文
UIGraphicsEndImageContext();
15、OC中自帶畫圖方法
// 1.得到當前的觸摸點
UITouch *touch = [touches anyObject];
CGPoint startPos = [touch locationInView:touch.view];
// 2.建立一個新的路徑
UIBezierPath *currenPath = [UIBezierPath bezierPath];//也可用alloc init方法建立
currenPath.lineCapStyle = kCGLineCapRound;
currenPath.lineJoinStyle = kCGLineJoinRound;
// 3.設置起點
[currenPath moveToPoint:startPos];
//4.連線
[currentPath addLineToPoint:endPos];
判斷一個點是否在一個矩形框內:
CGRectContainsPoint(rect,point);//判斷point這個點是否在rect這個矩形框內