Quartz 2D是一個二維繪圖引擎,同時支持iOS和Mac系統。
Quartz 2D能完成的工作:
1. 繪製圖形 : 線條\三角形\矩形\圓\弧等
2. 繪製文字
3. 繪製\生成圖片(圖像)
4. 讀取\生成PDF
5. 截圖\裁剪圖片
6. 自定義UI控件等
爲了便於搭建美觀的UI界面,iOS提供了UIKit框架,裏面有各種各樣的UI控件,利用UIKit框架提供的控件,能搭建和現實一些簡單、常見的UI界面。但是,有些UI界面極其複雜、而且比較個性化,用普通的UI控件無法實現,這時可以利用Quartz2D技術將控件內部的結構畫出來,自定義控件的樣子。其實,iOS中大部分控件的內容都是通過Quartz2D畫出來的,因此,Quartz2D在iOS開發中很重要的一個價值是:自定義view(自定義UI控件)。
圖形上下文
圖形上下文(Graphics Context):是一個CGContextRef類型的數據
圖形上下文的作用:
1. 保存繪圖信息、繪圖狀態
2. 決定繪製的輸出目標(繪製到什麼地方)(輸出目標可以是PDF文件、Bitmap或者顯示器的窗口上)
3. 相同的一套繪圖序列,指定不同的Graphics Context,就可將相同的圖像繪製到不同的目標上
**Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context**
自定義view
利用Quartz2D繪製東西到view上:
1. 要有圖形上下文,因爲它能保存繪圖信息,並且決定着繪製到什麼地方去
2. 那個圖形上下文必須跟view相關聯,才能將內容繪製到view上面
自定義view的步驟
- 新建一個類,繼承自UIView
- 實現- (void)drawRect:(CGRect)rect方法,然後在這個方法中取得跟當前view相關聯的圖形上下文繪製相應的圖形內容,利用圖形上下文將繪製的所有內容渲染顯示到view上面
drawRect:
爲什麼要實現drawRect:方法才能繪圖到view上?
因爲在drawRect:方法中才能取得跟view相關聯的圖形上下文drawRect:方法在什麼時候被調用?
當view第一次顯示到屏幕上時(被加到UIWindow上顯示出來)
調用view的setNeedsDisplay或者setNeedsDisplayInRect:時。
Quartz2D的API是純C語言的,Quartz2D的API來自於Core Graphics框架。數據類型和函數基本都以CG作爲前綴如:CGContextRef、CGPathRef、CGContextStrokePath(ctx)。
在drawRect:方法中取得上下文後,就可以繪製東西到view上。View內部有個layer(圖層)屬性,drawRect:方法中取得的是一個Layer Graphics Context,因此,繪製的東西其實是繪製到view的layer上去了。View之所以能顯示東西,完全是因爲它內部的layer。
Quartz2D繪圖的代碼步驟
1. 獲得圖形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
2. 拼接路徑(下面代碼是搞一條線段)
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);
3. 繪製路徑
CGContextStrokePath(ctx); // CGContextFillPath(ctx);
常用路徑函數
//新建一個起點
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)
//一般以CGContextDraw、CGContextStroke、CGContextFill開頭的函數,都是用來繪製路徑的
//Mode參數決定繪製的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
//繪製空心路徑
void CGContextStrokePath(CGContextRef c)
//繪製實心路徑
void CGContextFillPath(CGContextRef c)
如,畫一條線:
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//創建路徑
CGMutablePathRef path = CGPathCreateMutable();
//繪製路徑
CGPathMoveToPoint(path, NULL, 50, 50);
CGPathAddLineToPoint(path, NULL, 100,100);
//添加路徑至上下文
CGContextAddPath(ctx, path);
//渲染上下文
CGContextStrokePath(ctx);
}
繪製曲線:
- (void)drawRect:(CGRect)rect {
//繪製曲線
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 描述路徑
// 設置起點
CGContextMoveToPoint(ctx, 50, 50);
// cpx:控制點的x
CGContextAddQuadCurveToPoint(ctx, 150, 20, 250, 50);
// 渲染上下文
CGContextStrokePath(ctx);
}
常見屬性使用:
- (void)drawRect:(CGRect)rect {
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 描述路徑
//起點
CGContextMoveToPoint(ctx, 50, 50);
CGContextAddLineToPoint(ctx, 100, 50);
// 設置起點
CGContextMoveToPoint(ctx, 80, 60);
// 默認下一根線的起點就是上一根線終點
CGContextAddLineToPoint(ctx, 100, 200);
// 設置繪圖狀態,一定要在渲染之前
// 顏色
[[UIColor redColor] setStroke];
// 線寬
CGContextSetLineWidth(ctx, 5);
// 設置連接樣式
CGContextSetLineJoin(ctx, kCGLineJoinBevel);
// 設置頂角樣式
CGContextSetLineCap(ctx, kCGLineCapRound);
// 渲染上下文
CGContextStrokePath(ctx);
}
圖形上下文棧的操作
- 將當前的上下文copy一份,保存到棧頂(那個棧叫做”圖形上下文棧”)
void CGContextSaveGState(CGContextRef c) - 將棧頂的上下文出棧,替換掉當前的上下文
void CGContextRestoreGState(CGContextRef c)
感覺有點類似Android中的canvas.save()與canvas.restore();
矩陣操作
利用矩陣操作,能讓繪製到上下文中的所有路徑一起發生變化
1. 縮放
void CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
2. 旋轉
void CGContextRotateCTM(CGContextRef c, CGFloat angle)
3. 平移
void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
感覺可以和Android中canvas.scale()
、canvas.translate()
、canvas.rotate()
相比較,將當前上下文進行縮放旋轉和平移操作。
實現案例:
1、圖片水印:
實現方式:利用Quartz2D,將水印(文字、LOGO)畫到圖片的右下角
開啓一個基於位圖的圖形上下文
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
從上下文中取得圖片(UIImage)
UIImage* UIGraphicsGetImageFromCurrentImageContext();
結束基於位圖的圖形上下文
void UIGraphicsEndImageContext();
// 加載圖片
UIImage *image = [UIImage imageNamed:@"阿狸"];
// 0.獲取上下文,之前的上下文都是在view的drawRect方法中獲取(跟View相關聯的上下文layer上下文)
// 目前我們需要繪製圖片到新的圖片上,因此需要用到位圖上下文
// 怎麼獲取位圖上下文,注意位圖上下文的獲取方式跟layer上下文不一樣。位圖上下文需要我們手動創建。
// 開啓一個位圖上下文,注意位圖上下文跟view無關聯,所以不需要在drawRect.
// size:位圖上下文的尺寸(新圖片的尺寸)
// opaque: 不透明度 YES:不透明 NO:透明,通常我們一般都弄透明的上下文
// scale:通常不需要縮放上下文,取值爲0,表示不縮放
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 1.繪製原生的圖片
[image drawAtPoint:CGPointZero];
// 2.給原生的圖片添加文字
NSString *str = @"MrZk";
// 創建字典屬性
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSForegroundColorAttributeName] = [UIColor redColor];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
[str drawAtPoint:CGPointMake(200, 528) withAttributes:dict];
// 3.生成一張圖片給我們,從上下文中獲取圖片
UIImage *imageWater = UIGraphicsGetImageFromCurrentImageContext();
// 4.關閉上下文
UIGraphicsEndImageContext();
_imageView.image = imageWater;
2、 圖片裁剪
- (void)clipImage
{
// 0.加載圖片
UIImage *image = [UIImage imageNamed:@"阿狸頭像"];
// 1.開啓位圖上下文,跟圖片尺寸一樣大
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 2.設置圓形裁剪區域,正切與圖片
// 2.1創建圓形的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 2.2把路徑設置爲裁剪區域
[path addClip];
// 3.繪製圖片
[image drawAtPoint:CGPointZero];
// 4.從上下文中獲取圖片
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
// 5.關閉上下文
UIGraphicsEndImageContext();
_imageView.image = clipImage;
}
3、屏幕截屏
// 開啓位圖上下文
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把控件上的圖層渲染到上下文,layer只能渲染
[view.layer renderInContext:ctx];
// 生成一張圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關閉上下文
UIGraphicsEndImageContext();
4、圖片擦除
// 獲取當前點
CGPoint curP = [pan locationInView:self.view];
// 獲取擦除的矩形範圍
CGFloat wh = 100;
CGFloat x = curP.x - wh * 0.5;
CGFloat y = curP.y - wh * 0.5;
CGRect rect = CGRectMake(x, y, wh, wh);
// 開啓上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 控件的layer渲染上去
[_imageView.layer renderInContext:ctx];
// 擦除圖片
CGContextClearRect(ctx, rect);
// 生成一張圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
_imageView.image = image;
// 關閉上下文
UIGraphicsEndImageContext();