一、什麼是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 ]; }); } |