什么是Quartz2D
- 是一個(gè)二維的繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng)
- Quartz2D的API是純C語(yǔ)言的,它的API來(lái)自于Core Graphics框架,數(shù)據(jù)類(lèi)型和函數(shù),都是以CG開(kāi)頭的
Quartz2D的應(yīng)用
- 畫(huà)基本線(xiàn)條,繪制文字,圖片,截圖,自定義UIView
- 在開(kāi)發(fā)中,可以將內(nèi)部結(jié)構(gòu)比較復(fù)雜控件,通過(guò)繪制,實(shí)現(xiàn)自定義控件
Quartz2D中的圖形上下文是什么,有哪些類(lèi)型
- 圖形上下文是一個(gè)
CGContextRef
類(lèi)型的數(shù)據(jù) - 圖形上下文是用來(lái)保存用戶(hù)繪制的內(nèi)容狀態(tài),并決定繪制到那個(gè)地方
- 圖形上下文的類(lèi)型:
- Bitmap Graphics Context(位圖上下文)
- PDF Graphics Context
- Window Graphics Context
- Layer Graphics Context(圖層上下文,自定義UIView取得上下文就是圖層上下文.UIView之所以能夠顯示就是因?yàn)樗麅?nèi)部有一個(gè)圖層)
- Printer Graphics Context
自定義UIView的步驟
- 1.先自定義UIView
- 2.實(shí)現(xiàn)DrawRect方法
- 3.在DrawRect方法中取得跟View相關(guān)聯(lián)的上下文
- 4.繪制路徑(描述路徑長(zhǎng)什么樣).
- 5.把描述好的路徑保存到上下文(即:添加路徑到上下文)
- 6.把上下文的內(nèi)容渲染到View
- 注意:獲取的上下文,必須是與要繪制到的view相關(guān)聯(lián)的,這樣才能將上下文的內(nèi)容繪制到view上
基本線(xiàn)條的繪制
- 通過(guò)drawRect方法繪制
- DrawRect方法作用?什么時(shí)候調(diào)用.
- 作用 : 專(zhuān)用在這個(gè)方法當(dāng)中繪圖的.只有在這個(gè)方法當(dāng)中才能取得跟View相關(guān)聯(lián)的上下文.
- 調(diào)用 : 是系統(tǒng)自己調(diào)用的, 它是當(dāng)View顯示的時(shí)候自動(dòng)調(diào)用.
- 參數(shù)(rect): 指的是當(dāng)前view的bounds
- drawRect會(huì)自動(dòng)創(chuàng)建一個(gè)跟當(dāng)前View相關(guān)聯(lián)的上下文.
- View上的繪制以及顯示
在drawRect:方法中取得上下文后介衔,就可以繪制東西到view上
View內(nèi)部有個(gè)layer(圖層)屬性,drawRect:方法中取得的是一個(gè)Layer Graphics
Context,因此实束,繪制的東西其實(shí)是繪制到view的layer上去了
View之所以能顯示東西项钮,完全是因?yàn)樗鼉?nèi)部的layer
線(xiàn)段的繪制
- 步驟:
- 1.獲取當(dāng)前view的圖形上下文
- 2.描述路徑
- 2.1 設(shè)置路徑的起點(diǎn)
- 2.2 添加一根線(xiàn)到終點(diǎn)
- 3.將繪制的路徑添加到上下文中
- 4.把上下文中的內(nèi)容渲染到UIView的layer上(通過(guò)stoke或者fill的方式渲染)
-(void)drawRect:(CGRect)rect {
//1.獲取當(dāng)前上下文(uigraphics開(kāi)頭)
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.描述路徑
UIBezierPath *path = [UIBezierPath bezierPath];
//2.1設(shè)置起點(diǎn)
[path moveToPoint:CGPointMake(50, 200)];
//2.1添加一根線(xiàn)到終點(diǎn)
[path addLineToPoint:CGPointMake(200, 50)];
//3.把繪制的路徑添加到上下文
//UIBezierPath=UIKit -> CGPathRef=coreGraphics(將UIKit類(lèi)型的path轉(zhuǎn)換為CGPathRef類(lèi)型的path)
CGContextAddPath(ctx, path.CGPath);
//4.把上下文的內(nèi)容渲染UIView的layer.(stoke(描邊),fill(填充))
CGContextStrokePath(ctx);
}
- 設(shè)置/修改上下文的狀態(tài).
設(shè)置線(xiàn)的寬度
CGContextSetLineWidth(ctx, 10);
設(shè)置線(xiàn)的連接樣式.
CGContextSetLineJoin(ctx, kCGLineJoinBevel);
設(shè)置線(xiàn)的頂角樣式
CGContextSetLineCap(ctx, kCGLineCapRound);
設(shè)置線(xiàn)的顏色.還可以直接用set這種方法
[[UIColor greenColor] set];
- 如果在添加另一根線(xiàn)
- 第一種方法:重新設(shè)置起點(diǎn),添加一根線(xiàn)到某個(gè)點(diǎn),一個(gè)UIBezierPath路徑上面可以有多條線(xiàn).
- 第二種方法:直接在原來(lái)的基礎(chǔ)上添加線(xiàn).把上一條線(xiàn)的終點(diǎn)當(dāng)做下一條線(xiàn)的起點(diǎn).添加一根線(xiàn)到某個(gè)點(diǎn)直接在下面addLineToPoint:CGPointMake(200, 50)
曲線(xiàn)的繪制
-(void)drawQuadCurve {
//1.獲取當(dāng)前上下文(uigraphics開(kāi)頭)
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.描述路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50, 250)];
//添加一根曲線(xiàn)
[path addQuadCurveToPoint:CGPointMake(250, 250) controlPoint:CGPointMake(150, 50)];
//3.把繪制的路徑添加到上下文
//UIBezierPath=UIKit -> CGPathRef=coreGraphics
CGContextAddPath(ctx, path.CGPath);
//4.把上下文的內(nèi)容渲染到View的layer
CGContextStrokePath(ctx);
}
矩形的繪制
//畫(huà)矩形
- (void)drawRect {
//1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.描述路徑
/*
(x,y)點(diǎn)決定了矩形左上角的點(diǎn)在哪個(gè)位置
(width,height)是矩形的寬度高度
*/
// 普通的矩形 (bezierPathWithRect:)
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 200)];
//圓角矩形 (bezierPathWithRoundedRect:cornerRadius:)
//cornerRadius:圓角半徑
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 200) cornerRadius:100];
//單獨(dú)設(shè)置某一角的圓角(bezierPathWithRoundedRect:byRoundingCorners:cornerRadius:)
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 100) byRoundingCorners:UIRectCornerTopLeft cornerRadius:CGSizeMake(10, 20)];
//設(shè)置線(xiàn)段的顏色
[[UIColor yellowColor] set];
//3.把路徑添加到上下文
CGContextAddPath(ctx, path.CGPath);
//4.把上下文的內(nèi)容渲染View的layer
CGContextFillPath(ctx);
}
橢圓形的繪制
//畫(huà)橢圓形 bezierPathWithOvalInRect:
//1.獲取上下文
CGContextRef ctf = UIGraphicsGetCurrentContext();
//2.描述路徑
UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 200, 100)];
//3.把路徑添加到上下文
CGContextAddPath(ctf, path.CGPath);
//4.把上下文的內(nèi)容渲染View的layer
CGContextStrokePath(ctf);
弧形的繪制
//畫(huà)弧
-(void)drawRect:(CGRect)rect
{
//Center:圓心
//radius:半徑
//startAngle:開(kāi)始角度,0度在圓的最右側(cè).往下,角度為正,往上,角度為負(fù)
//endAngle:截至角度
//clockwise:是否為順時(shí)針?lè)较? CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
CGFloat radius = rect.size.width * 0.5 - 10;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:-M_PI_2 clockwise:NO];
[path stroke];
}
扇形的繪制
- 繪制空心扇形
//先繪制一個(gè)弧形
//設(shè)置弧形的中心點(diǎn)
CGPoint center = CGPointMake(self.bounds.size.width * 0.5, rect.size.height * 0.5);
//設(shè)置弧形的半徑
CGFloat radius = rect.size.width * 0.5 - 10;
//繪制弧形曲線(xiàn)
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:-M_PI_2 clockwise:NO];
//繪制扇形
//添加一根線(xiàn)到圓心
[path addLineToPoint:center];
[[UIColor redColor] set];
//關(guān)閉路徑(自動(dòng)從終點(diǎn)連接一根線(xiàn)到起點(diǎn).)
[path closePath];
//渲染到view的layer上
[path stroke];
- 繪制實(shí)心扇形
- 通過(guò)[path fill],這個(gè)填充方法會(huì)自動(dòng)將路徑關(guān)閉,并填充圖形內(nèi)部分
//先繪制一個(gè)弧形
//設(shè)置弧形的中心點(diǎn)
CGPoint center = CGPointMake(self.bounds.size.width * 0.5, rect.size.height * 0.5);
//設(shè)置弧形的半徑
CGFloat radius = rect.size.width * 0.5 - 10;
//繪制弧形曲線(xiàn)
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:-M_PI_2 clockwise:NO];
//繪制扇形
//添加一根線(xiàn)到圓心
[path addLineToPoint:center];
[[UIColor redColor] set];
//填充模式會(huì)自動(dòng)關(guān)閉路徑
[path fill];
圖形的繪制以及渲染的方式
- 1.通過(guò)獲取圖形上下文的方式,拼接路徑,將路徑添加到上下文,將上下文的內(nèi)容渲染到View的Layer中
```
以弧形繪制的代碼為例:
// 1.獲取上下文
CGContextRef ctf = UIGraphicsGetCurrentContext();
// 2.繪制路徑
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.width * 0.5) radius:100 startAngle:0 endAngle:M_PI clockwise:YES];
// 3.將路徑添加到上下文
CGContextAddPath(ctf, path.CGPath);
// 4.將上下文中的內(nèi)容渲染到View的Layer上
CGContextStrokePath(ctf);
```
- 2.通過(guò)UIKit封裝的方法進(jìn)行上下文的畫(huà)圖
直接來(lái)個(gè): [path stroke];或者[path fill];就可以了.
它底層的實(shí)現(xiàn),就是獲取上下文,拼接路徑,把路徑添加到上下文,渲染到View//繪制路徑 CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5); CGFloat radius = rect.size.width * 0.5 - 10; UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:-M_PI_2 clockwise:NO]; //渲染到view的layer上 [path stroke];
繪制文字
-
1.使用UIKit提供的方法進(jìn)行繪制.
方法說(shuō)明:
1. drawAtPoint:要畫(huà)到哪個(gè)位置
withAttributes:文本的樣式.
[str drawAtPoint:CGPointZero withAttributes:nil];2. drawInRect:要將文字繪制到哪個(gè)區(qū)域 withAttributes:文本的樣式. [str drawInRect:rect withAttributes:nil];
2.drawAtPoint:和drawInRect:的區(qū)別?
drawAtPoint:不能夠自動(dòng)換行
drawInRect:能夠自動(dòng)換行
//1.設(shè)置要繪制的文字
NSString * str = @"繪制文字練習(xí)繪制文字練習(xí)繪制文字練習(xí)繪制文字練習(xí)繪制文字練習(xí)";
//2.設(shè)置文字的屬性,通過(guò)Attribute設(shè)置
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
//設(shè)置文字字體
dict[NSFontAttributeName] = [UIFont systemFontOfSize:45];
//設(shè)置文字顏色
dict[NSForegroundColorAttributeName] = [UIColor yellowColor];
//設(shè)置文字邊的寬度
dict[NSStrokeWidthAttributeName] = @5;
//設(shè)置文字的描邊
dict[NSStrokeColorAttributeName] = [UIColor greenColor];
//設(shè)置文字的陰影
NSShadow * shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor cyanColor];
shadow.shadowOffset = CGSizeMake(10, 10);
shadow.shadowBlurRadius = 10;
dict[NSShadowAttributeName] = shadow;
//3.將文字繪制到當(dāng)前view 的layer上
[str drawAtPoint:CGPointZero withAttributes:nil];
[str drawInRect:rect withAttributes:dict];
繪制圖片
繪制圖片同樣開(kāi)始要先把圖片素材導(dǎo)入.
-
1.使用UIKit提供的方法進(jìn)行繪制.
方法說(shuō)明:
1. drawAtPoint:要畫(huà)到哪個(gè)位置
AtPoint:參數(shù)說(shuō)明圖片要繪制到哪個(gè)位置.
通過(guò)調(diào)用UIKit的方法drawAtPoint:CGPointZero方法進(jìn)行繪制;
[str drawAtPoint:CGPointZero];2. drawInRect:要將圖片繪制到哪個(gè)區(qū)域 AtPoint:參數(shù)說(shuō)明圖片要繪制到哪塊區(qū)域. [str drawInRect:rect];
在繪制圖片過(guò)程當(dāng)中.drawAtPoint:和drawInRect:兩個(gè)方法的區(qū)別?
drawAtPoint:繪制出來(lái)的圖圖片跟圖片的實(shí)際尺寸一樣大
drawInRect:使用這個(gè)方法繪制出來(lái)的圖片尺寸會(huì)和傳入的rect區(qū)域一樣大.
//1.加載圖片
UIImage *image = [UIImage imageNamed:@"001"];
//會(huì)把超過(guò)裁剪區(qū)域以外的內(nèi)容給裁剪掉/必須得要在繪制之前設(shè)置裁剪區(qū)域
UIRectClip(CGRectMake(0, 0, 50, 50));
//drawAtPoint:繪制的是原始圖片的尺寸大小.
[image drawAtPoint:CGPointZero];
//把繪制的圖片填充到給的區(qū)域當(dāng)中.
[image drawInRect:rect];
//平鋪
[image drawAsPatternInRect:rect];
[[UIColor redColor] set];
//快速填充一個(gè)區(qū)域
UIRectFill(CGRectMake(50, 50, 100, 100));
模擬系統(tǒng)的UIImageView
- 實(shí)現(xiàn)步驟
1.自定義一個(gè)UIView
2.定義一個(gè)UIImage公共的屬性,用于接收外界傳遞的圖片
3.重寫(xiě)圖片屬性的set方法,每次傳入圖片后,進(jìn)行重繪
4.定義一個(gè)帶有image參數(shù)的構(gòu)造方法,在實(shí)現(xiàn)構(gòu)造方法的時(shí)候,將view的size設(shè)置為image的size,并將image賦值給image屬性,這樣顯示的 view就有了尺寸,并且尺寸和圖片的尺寸相同
5.在drawRect方法中,將外界傳入的圖片繪制到當(dāng)前view的Layer上
#import <UIKit/UIKit.h>
@interface ZHJImageView : UIView
//用于接收外界傳入的圖片
@property (nonatomic, strong) UIImage * image;
//在初始化View的時(shí)候,將view的尺寸設(shè)置為傳入的image的尺寸
-(instancetype)initWithImage:(UIImage *)image;
@end
@implementation ZHJImageView
//當(dāng)image屬性接收到傳入的圖片的時(shí)候,立刻調(diào)用drawRect方法,進(jìn)行重繪
-(void)setImage:(UIImage *)image
{
_image = image;
//重繪
[self setNeedsDisplay];
}
//初始化的時(shí)候,讓當(dāng)前view的尺寸等于傳入的圖片的尺寸
-(instancetype)initWithImage:(UIImage *)image{
if (self = [super init]) {
//將圖片賦值給image屬性,并在屬性的set方法中重繪
_image = image;
self.frame = CGRectMake(0, 0, self.image.size.width, self.image.size.height);
}
return self;
}
//將傳入的圖片繪制到view的layer上
-(void)drawRect:(CGRect)rect
{
[self.image drawInRect:rect];
}
@end
CADisplayLink的使用(雪花飄落)
-
在重繪時(shí),NSTimer和CADisplayLink的區(qū)別
setNeedsDisplay底層會(huì)調(diào)用DrawRect方法重繪.
但是它不是立馬就進(jìn)行重繪.它僅僅是設(shè)置了一個(gè)重繪標(biāo)志,等到下一次屏幕刷新(屏幕的刷新次數(shù)是每分鐘60次)的時(shí)候才會(huì)調(diào)用DrawRect方法.如果使用NSTime的話(huà),假設(shè)是0.01調(diào)用一次重繪.假設(shè)屏幕0.02秒的時(shí)候它才刷新一次.中間就會(huì)等0.01秒. 也就是每次都會(huì)等0.01秒這樣累加上去.讓變的越來(lái)越卡頓. 使用CADisplayLink的話(huà),它的定時(shí)器方法就是屏幕每次刷新的時(shí)候就會(huì)調(diào)用(通常屏幕一秒鐘刷新60次) 它和setNeedsDisplay調(diào)用DrawRect方法的時(shí)機(jī)正好吻合,不會(huì)出間等待間隔.不會(huì)出現(xiàn)屏幕卡頓現(xiàn)象.
#import "SnowFlowView.h"
@implementation SnowFlowView
//因?yàn)檫@個(gè)view是從storyboard中加載的,所以將添加子控件的代碼寫(xiě)在awakeFromNib中
-(void)awakeFromNib
{
//創(chuàng)建定時(shí)器
CADisplayLink * link = [CADisplayLink displayLinkWithTarget:self selector:@selector(flowSnow)];
//將定時(shí)器加入到主線(xiàn)程中
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
//定義另個(gè)靜態(tài)全局變量,用于保存圖片位置
static int snowX = 0;
static int snowY = 0;
//定時(shí)器方法
-(void)flowSnow{
//實(shí)現(xiàn)雪花下落的效果
snowX += 1;
if (snowX >= [UIScreen mainScreen].bounds.size.width) {
snowX = 0;
}
snowY += 10;
if (snowY >= [UIScreen mainScreen].bounds.size.height) {
snowY = 0;
}
//重繪
[self setNeedsDisplay];
}
//將雪花圖片繪制到當(dāng)前的view上
-(void)drawRect:(CGRect)rect
{
UIImage * image = [UIImage imageNamed:@"雪花"];
[image drawAtPoint:CGPointMake(snowX, snowY)];
}
@end
圖形上下文狀態(tài)棧
我們獲取的圖層上下文當(dāng)中其實(shí)有兩塊區(qū)域
一個(gè)是存放添加的路徑
一個(gè)是用來(lái)保存用戶(hù)設(shè)置的狀態(tài),這些狀態(tài)包括線(xiàn)條的顏色,線(xiàn)寬等.
當(dāng)我們把上下文的內(nèi)容渲染到View上面的時(shí)候,它會(huì)自動(dòng)將設(shè)置的所有上下文狀態(tài)運(yùn)行到保存的路徑上面顯示到View上面.上下文狀態(tài)棧
上下文狀態(tài)棧為內(nèi)存中的一塊區(qū)域,它用來(lái)保存當(dāng)前上下文的狀態(tài),這里所謂的上下文的狀態(tài),其實(shí)就是在上下文中設(shè)置的線(xiàn)條的顏色,寬度等,當(dāng)再次去出上下文的狀態(tài)的時(shí)候,也就是取出了當(dāng)時(shí)上下文中設(shè)置的線(xiàn)寬,顏色等屬性給新繪制的線(xiàn)段使用-
如何存儲(chǔ)和獲取圖形上下文的狀態(tài)
- 1.把上下文狀態(tài)保存到上下文狀態(tài)棧
CGContextSaveGState(ctx); - 2.從上下文狀態(tài)棧中取出上下文狀態(tài)
CGContextRestoreGState(ctx);
- 1.把上下文狀態(tài)保存到上下文狀態(tài)棧
圖形上下文棧使用的示例
-(void)drawRect:(CGRect)rect
{
//繪制第一條線(xiàn)
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath * path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(20, 150)];
[path addLineToPoint:CGPointMake(280, 150)];
//第一次設(shè)置圖形上下文的狀態(tài)
CGContextSetLineWidth(ctx, 10);
[[UIColor cyanColor] set];
CGContextSaveGState(ctx);//保存第一次設(shè)置的上下文的狀態(tài)
//第二次設(shè)置圖形上下文的狀態(tài)
CGContextSetLineWidth(ctx, 5);
[[UIColor greenColor] set];
CGContextSaveGState(ctx);//保存第二次設(shè)置的上下文的狀態(tài)
//渲染第一次繪制的路徑
CGContextAddPath(ctx, path.CGPath);
CGContextStrokePath(ctx);
//繪制第二條線(xiàn)
UIBezierPath * path1 = [[UIBezierPath alloc] init];
[path1 moveToPoint:CGPointMake(150, 20)];
[path1 addLineToPoint:CGPointMake(150, 280)];
//從位圖上下文狀態(tài)棧中取出位圖上下文的狀態(tài)(取出第一次保存的位圖上下文的狀態(tài))
CGContextRestoreGState(ctx);//取出第二次圖形上下文的狀態(tài)
CGContextRestoreGState(ctx);//取出第一次圖像上下文的狀態(tài)
//渲染第二次繪制的路徑
CGContextAddPath(ctx, path1.CGPath);
CGContextStrokePath(ctx);
}
圖形上下文矩陣的操作(對(duì)上下文的路徑進(jìn)行的一些操作,如平移喳张、旋轉(zhuǎn)津辩、縮放)
- 使用注意點(diǎn):
1.想要做上下文的形變操作必須得獲取上下文.
2.形變操作要在添加路徑之前進(jìn)行.
3.旋轉(zhuǎn)不是以繪制的圖形的中心旋轉(zhuǎn)(有可能是以獲取的上下文的左上角為原點(diǎn)進(jìn)行旋轉(zhuǎn)的,通過(guò)"CGContextRotateCTM(ctx, M_PI_4);"代碼可以發(fā)現(xiàn),旋轉(zhuǎn)的是獲取的圖形上下文)
- (void)drawRect:(CGRect)rect {
//獲取當(dāng)前的上下文.
CGContextRef ctx = UIGraphicsGetCurrentContext();
//畫(huà)一個(gè)橢圓
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-100, -50, 200, 100)];
//上下文的矩陣操作,就是可以上下文當(dāng)中的路徑進(jìn)行一些形變操作.
//平移操作
CGContextTranslateCTM(ctx, 100, 50);
//縮放操作
CGContextScaleCTM(ctx, 0.5, 0.5);
//旋轉(zhuǎn)操作
CGContextRotateCTM(ctx, M_PI_4);
//注意形變操作要在添加路徑之前進(jìn)行.
[[UIColor redColor]set];
//把路徑添加到上下文.
CGContextAddPath(ctx, path.CGPath);
//把上下文的內(nèi)容渲染到View.
CGContextFillPath(ctx);
}