天天看點

11-Quartz2D 二維繪圖引擎

一、什麼是Quartz2D

 Quartz 2D是⼀個二維繪圖引擎,同時支援iOS和Mac系統

 Quartz 2D能完成的工作:

  繪制圖形 : 線條\三角形\矩形\圓\弧等

  繪制文字

  繪制\生成圖檔(圖像)

  讀取\生成PDF

  截圖\裁剪圖檔

  自定義UI控件 

二、Quartz2D在iOS開發中的價值

為了便于搭建美觀的UI界面,iOS提供了UIKit架構,⾥⾯有各種各樣的UI控件 

UILabel:顯⽰文字

UIImageView:顯示圖檔

UIButton:同時顯示圖檔和⽂字(能點選)

利⽤UIKit架構提供的控件,拼拼湊湊,能搭建和現實一些簡單、常見的UI界⾯

但是,有些UI界面極其複雜、⽽且⽐較個性化,⽤普通的UI控件無法實作,這時可以利用Quartz2D技術将控件内部的結構畫出來,自定義控件的樣子

其實,iOS中⼤部分控件的内容都是通過Quartz2D畫出來的 是以,Quartz2D在iOS開發中很重要的⼀個價值是:自定義view(自定義UI控件)

三、圖形上下文

圖形上下文(Graphics Context):是一個CGContextRef類型的資料

圖形上下文的作用:

(1)儲存繪圖資訊、繪圖狀态

(2)決定繪制的輸出目标(繪制到什麼地⽅去?) (輸出目标可以是PDF⽂檔案、Bitmap或者顯示器的視窗上)

相同的⼀套繪圖序列,指定不同的Graphics Context,就可将相同的圖像繪制到不同的目标上 

四、自定義view

如何利用Quartz2D⾃定義view?(⾃定義UI控件)

如何利用Quartz2D繪制東西到view上?

首先,得有圖形上下文,因為它能儲存繪圖資訊,并且決定着繪制到什麼地方去

其次,那個圖形上下⽂必須跟view相關聯,才能将内容繪制到view上面

⾃定義view的步驟:

(1)建立⼀個類,繼承自UIView

(2)實作-(void)drawRect:(CGRect)rect⽅法.然後在這個⽅方法中 :

         1)取得跟目前view相關聯的圖形上下文;    UIGraphicsGetCurrentContext ()

         2)繪制相應的圖形内容

         3)利用圖形上下文将繪制的所有内容渲染顯示到view上面   CGContextFillPath(ctx);

五、補充說明

1.drawRect:

(1)為什麼要實作drawRect:⽅法才能繪圖到view上?

因為在drawRect:⽅法中才能取得跟view相關聯的圖形上下文

(2)drawRect:⽅法在什麼時候被調用?

當view第一次顯示到螢幕上時(被加到UIWindow上顯示出來)

調用view的setNeedsDisplay或者setNeedsDisplayInRect:時 

2.Quartz2D須知

Quartz2D的API是純C語⾔言的

Quartz2D的API來自于Core Graphics架構

資料類型和函數基本都以CG作為字首

CGContextRef

CGPathRef

CGContextStrokePath(ctx); 

3.drawRect:中取得的上下⽂文

在drawRect:方法中取得上下文後,就可以繪制東西到view上

View内部有個layer(圖層)屬性,drawRect:方法中取得的是一個Layer Graphics Context,是以,繪制的東西其實是繪制到view的layer上去了

View之是以能顯示東西,完全是因為它内部的layer 

六、代碼示例

---------------------------------------------線段|矩形|圓|圓弧--------- -------------------------------------------------  

- ( void )drawCurve:( CGContextRef )ctx

{

    // 設定起點     CGContextMoveToPoint(ctx,20, 100);       // 三次曲線

    // CGFloat cp1x, // 控制點 1 x 坐标

    // CGFloat cp1y, // 控制點 1 y 坐标

    // CGFloat cp2x, // 控制點 2 x 坐标

    // CGFloat cp2y, // 控制點 2 y 坐标

    // CGFloat x,  // 直線的終點 x 坐标

    // CGFloat y  // 直線的終點 y 坐标

    // 添加曲線

    CGContextAddCurveToPoint (ctx, 100 , 20 , 20 , 80 , 300 , 50 );

    // 二次曲線

    // CGFloat cpx, 控制點 x 坐标

    // CGFloat cpy, 控制點 y 坐标

    // CGFloat x,  直線的終點 x 坐标

    // CGFloat y  直線的終點 y 坐标

   CGContextAddQuadCurveToPoint (ctx, 5 , 10 , 30 , 20 );

    // 繪制

    CGContextDrawPath (ctx, kCGPathStroke );

    }

- ( void )drawArc:( CGContextRef )ctx

{

    // x\y : 圓心

    // radius : 半徑

    // startAngle : 開始角度

    // endAngle : 結束角度

    // clockwise : 圓弧的伸展方向 (0: 順時針 , 1: 逆時針 )

    CGContextAddArc (ctx, 100 , 200 , 20 , 0 , M_PI , 1 );

    CGContextClosePath (ctx);

    [[ UIColor redColor ] setStroke ];

    [[ UIColor orangeColor ] setFill ];

    CGContextDrawPath (ctx, kCGPathFillStroke );

}

- ( void )drawCircle:( CGContextRef )ctx

{

    CGRect rect = CGRectMake ( 80 , 50 , 200 , 100 );

    CGContextAddEllipseInRect (ctx, rect);

    [[ UIColor redColor ] setFill ];

    [[ UIColor blueColor ] setStroke ];

    CGContextDrawPath (ctx, kCGPathFillStroke );

}

- ( void )drawRec:( CGContextRef )ctx

{

   CGRect rect = CGRectMake ( 100 , 50 , 200 , 50 );

//    CGContextAddRect(ctx, rect);

    CGContextStrokePath (ctx);

    [[ UIColor redColor ] setFill ];

    UIRectFrame (rect);

    UIRectFill (rect);

}

- ( void )drawLine2:( CGContextRef )ctx

{

    CGPoint p1 = CGPointMake ( 100 , 100 );

    CGPoint p2 = CGPointMake ( 100 , 150 );

    CGPoint p3 = CGPointMake ( 300 , 150 );

    CGPoint p4 = CGPointMake ( 300 , 100 );

    CGPoint p5 = CGPointMake ( 100 , 100 );

    CGPoint points[] = {p1, p2, p3, p4, p5};

    CGContextAddLines (ctx, points, 5 );

    [[ UIColor redColor ] setStroke ];

    [[ UIColor greenColor ] setFill ];

    CGContextDrawPath (ctx, kCGPathFillStroke );

}

--------------------------------------------------------線段---- ----------------------------------------------------

- ( void )drawLine1:( CGContextRef )ctx

{

    CGMutablePathRef path = CGPathCreateMutable ();

    CGPathMoveToPoint (path, NULL , 50 , 50 );

    CGPathAddLineToPoint (path, NULL , 50 , 200 );

    CGPathAddLineToPoint (path, NULL , 250 , 250 );

    CGPathCloseSubpath (path);

    CGContextAddPath (ctx, path);

    CGContextSetRGBStrokeColor (ctx, 255 / 255.0 , 97 / 255.0 , 0 / 255.0 , 1 );

    CGContextSetLineWidth (ctx, 5 );             CGContextStrokePath (ctx);

    CGPathRelease (path);     }

--------------------------------------------------------文字|圖檔---- ----------------------------------------------------         使用OC文法畫圖不用使用上下文          

           NSString *str =@" 哈哈 " ;    

    //[str drawAtPoint:CGPointZero withAttributes:nil];        NSDictionary *dict =@{NSForegroundColorAttributeName:[UIColorredColor]};    

   [str drawInRect:CGRectMake(20,20, 100, 50) withAttributes:dict];

    UIImage *image = [ UIImage imageNamed : @"pig" ];         // 方式一:指定繪圖的位置,大小有圖檔決定     [image drawAtPoint : CGPointMake ( 80 , 80 )];        

    [image drawInRect : CGRectMake ( 0 , 0 , 60 , 60 )];        

    [image drawAsPatternInRect:CGRectMake(0,0, 200,200)];

------------------------------------------------ 使用 CoreGraphics架構---- ------------------------------------------------     //1. 儲存原有的坐标體系        //将上下文儲存一份放到棧中     CGContextSaveGState (context);

    //2. 縮放 x 和 Y 軸

    CGContextScaleCTM (context, 1 , - 1 );

    //3. 平移圖檔

    CGContextTranslateCTM (context, 0 , - 200 );

    //4. 使用 Core Graphic 繪制圖像

    CGContextDrawImage (context, CGRectMake ( 0 , 0 , 200 , 200 ), img. CGImage );

        //5. 還原坐标體系         //将棧頂得上下文出棧 替換目前的上下文     CGContextRestoreGState(context);

--------------------------------------------------------圖檔裁剪---- ----------------------------------------------------   為image添加類目 裁剪圖檔方法

+ ( instancetype )circleImageWithName:( NSString *)name borderWidth:( CGFloat )borderWidth borderColor:( UIColor *)borderColor;

+ ( instancetype )circleImageWithName:( NSString *)name borderWidth:( CGFloat )borderWidth borderColor:( UIColor *)borderColor

{

    // 1. 加載原圖

    UIImage *oldImage = [ UIImage imageNamed :name];

    // 2. 開啟上下文

    CGFloat imageW = oldImage. size . width + 2 * borderWidth;

    CGFloat imageH = oldImage. size . height + 2 * borderWidth;

    CGSize imageSize = CGSizeMake (imageW, imageH);

    UIGraphicsBeginImageContextWithOptions (imageSize, NO , 0.0 );

    // 3. 取得目前的上下文

    CGContextRef ctx = UIGraphicsGetCurrentContext ();

    // 4. 畫邊框 ( 大圓 )

    [borderColor 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 - borderWidth;

    CGContextAddArc (ctx, centerX, centerY, smallRadius, 0 , M_PI * 2 , 0 );

    // 裁剪 ( 後面畫的東西才會受裁剪的影響 )

    CGContextClip (ctx);

    // 6. 畫圖

    [oldImage drawInRect : CGRectMake (borderWidth, borderWidth, oldImage. size . width , oldImage. size . height )];

    // 7. 取圖

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext ();

    // 8. 結束上下文

    UIGraphicsEndImageContext ();

    return newImage;

}

--------------------------------------------------------打水印---- ----------------------------------------------------   為image添加類目 打水印方法

@interface UIImage (MJ)

+ ( instancetype )waterImageWithBg:( NSString *)bg logo:( NSString *)logo; @end

@implementation UIImage (MJ)

+ ( instancetype )waterImageWithBg:( NSString *)bg logo:( NSString *)logo

{

    UIImage *bgImage = [ UIImage imageNamed :bg];

    // 1. 建立一個基于位圖的上下文 ( 開啟一個基于位圖的上下文 )

    UIGraphicsBeginImageContextWithOptions (bgImage. size , NO , 0.0 );

    // 2. 畫背景

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

    return newImage; }

--------------------------------------------------------螢幕截圖---- ---------------------------------------------------- + ( instancetype )captureWithView:( UIView *)view;  

+ ( instancetype )captureWithView:( UIView *)view

{

    // 1. 開啟上下文

    UIGraphicsBeginImageContextWithOptions (view. frame . size , NO , 0.0 );

    // 2. 将控制器 view 的 layer 渲染到上下文

    [view. layer renderInContext : UIGraphicsGetCurrentContext ()];

    // 3. 取出圖檔

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext ();

    // 4. 結束上下文

    UIGraphicsEndImageContext ();

    return newImage; }

調用: - ( IBAction )clip {     dispatch_after ( dispatch_time ( DISPATCH_TIME_NOW , ( int64_t )( 1.0 * NSEC_PER_SEC )), dispatch_get_main_queue (), ^{

        // 1. 捕捉

        UIImage *newImage = [ UIImage captureWithView : self . view ];

        // 2. 寫檔案

        NSData *data = UIImagePNGRepresentation (newImage);

        NSString *path = [[ NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ) lastObject ] stringByAppendingPathComponent : @"new.png" ];

        [data writeToFile :path atomically : YES ];

    }); }