iOS開發之Quartz2D

一、         Quartz2D概述及做用app

Quartz2D的API是純C語言的,Quartz2D的API來自於Core Graphics框架。框架

 

數據類型和函數基本都以CG做爲前綴,好比:函數

CGContextRefatom

CGPathRef對象

CGContextStrokePath(ctx);繼承

……遊戲

Quartz 2D是一個二維繪圖引擎,同時支持iOS和Mac系統。圖片

Quartz 2D能完成的工做:ip

繪製圖形 : 線條\三角形\矩形\圓\弧等;內存

繪製文字;

繪製\生成圖片(圖像);

讀取\生成PDF;

截圖\裁剪圖片;

自定義UI控件;

… …

2Quartz2DiOS開發中的價值

爲了便於搭建美觀的UI界面,iOS提供了UIKit框架,裏面有各類各樣的UI控件,好比:

UILabel:顯示文字;

UIImageView:顯示圖片;

UIButton:同時顯示圖片和文字(能點擊);

… …

利用UIKit框架提供的控件,拼拼湊湊,能搭建和現實一些簡單、常見的UI界面。

可是,有些UI界面極其複雜、並且比較個性化,用普通的UI控件沒法實現,這時能夠利用Quartz2D技術將控件內部的結構畫出來,自定義控件的樣子。其實,iOS中大部分控件的內容都是經過Quartz2D畫出來的。所以,Quartz2DiOS開發中很重要的一個價值是:自定義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);

5drawRect

爲何要實現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、經常使用繪製路徑函數

  • Mode參數決定繪製的模式

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)

11Quartz2D的內存管理

關於內存管理,有如下原則:

(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();

15OC中自帶畫圖方法

// 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這個矩形框內

相關文章
相關標籤/搜索