一直想讓自己每天都能進步全景,但是學習這個事情一直都無法堅持艾蓝,只有自己感覺到了緊迫感,才會敲起代碼粪牲,研究自己以前沒接觸過的知識古瓤,寫博客的目的就是希望能夠和更多的朋友一起討論,促進交流腺阳,共同進步B渚!亭引!
什么是Quartz2D
Quartz2D的API是純C語言的绎速,它是一個二維繪圖引擎,同時支持iOS和Mac系統(tǒng)焙蚓。Quartz2D的API來自于Core Graphics
框架纹冤,數(shù)據(jù)類型和函數(shù)基本都以CG作為前綴:CGContextRef
洒宝、CGPathRef
等。實際開發(fā)中UIKit
框架能夠幫我們完成大部分UI萌京,但是有些UI比較復雜雁歌,普通的UIView無法滿足我們的開發(fā)需要,這是就可以利用Quartz2D
技術(shù)畫出我們想要的控件知残。
DrawRect:
首先我們需要知道靠瞎,- (void)drawRect:(CGRect)rect
方法是在什么時候調(diào)用:
我們發(fā)現(xiàn)該方法是在視圖即將展示的時候調(diào)用的,在這個方法中可以獲取到上下文橡庞,將試圖繪制到View上较坛,其實每一個View內(nèi)部都有一個layer
屬性,這個方法中就可以了取得一個layer,所以我們繪制的東西其實是繪制到View的layer上扒最,試圖之所以能夠顯示東西,就是因為他的內(nèi)部有一個layer
所以之所以實現(xiàn)drawRect:方法华嘹,是因為吧趣,在這個方法當中可以獲取到圖形上下文,之后耙厚,才會View才會展示出來
- (void)drawRect:(CGRect)rect
的調(diào)用時機:
- 當view第一次顯示到屏幕上時(被加到UIWindow上顯示出來)
- 調(diào)用view的
setNeedsDisplay
或者setNeedsDisplayInRect:
時
圖形上下文(Graphics Context)
- 保存繪圖信息强挫、繪圖狀態(tài)
- 決定繪制的輸出目標(繪制到什么地方去?)
- (輸出目標可以是PDF文件薛躬、Bitmap或者顯示器的窗口上)
- 相同的一套繪圖序列俯渤,指定不同的Graphics Context,就可將相同的圖像繪制到不同的目標上
繪制好的圖形會保存到圖像上下文中去型宝,圖像上下文中的繪制的圖像又會顯示在我們指定的輸出目標當中
Quartz2D提供的幾種上下文類型:
- Bitmap Graphics Context
- PDF Graphics Context
- Window Graphics Context
- Layer Graphics Context
- Printer Graphics Context
Quartz2D繪圖的基本步驟
- 獲得圖形上下文
- 拼接路徑(下面代碼是搞一條線段八匠,添加矩形、橢圓趴酣、圓弧的代碼可以查看具體的APi梨树,用法基本相同)
- 繪制路徑
代碼示例:
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);
CGContextStrokePath(ctx); // CGContextFillPath(ctx);
圖形上下文棧的操作
將當前的上下文copy一份,保存到棧頂(那個棧叫做”圖形上下文棧”岖寞,先進后出)
void CGContextSaveGState(CGContextRef c)
將棧頂?shù)纳舷挛某鰲?替換掉當前的上下文
void CGContextRestoreGState(CGContextRef c)
Quartz2D的內(nèi)存管理
- 使用含有“Create”或“Copy”的函數(shù)創(chuàng)建的對象抡四,使用完后必須釋放,否則將導致內(nèi)存泄露
- 使用不含有“Create”或“Copy”的函數(shù)獲取的對象仗谆,則不需要釋放
- 如果retain了一個對象指巡,不再使用時,需要將其release掉
- 可以使用Quartz 2D的函數(shù)來指定retain和release一個對象隶垮。例如藻雪,如果創(chuàng)建了一個CGColorSpace對象,則使用函數(shù)CGColorSpaceRetain和CGColorSpaceRelease來retain和release對象岁疼。
- 也可以使用Core Foundation的CFRetain和CFRelease阔涉。注意不能傳遞NULL值給這些函數(shù)
實戰(zhàn)-畫板工具
了解了Quartz2D
的基本知識后缆娃,我們就可以運用這些知識,進行一些綜合的練習.
主要代碼如下
- 添加手勢
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:pan];
實現(xiàn)方法:
- (void)pan:(UIPanGestureRecognizer *)pan{
// 獲取當前位置所在點
CGPoint curP = [pan locationInView:self];
// 筆尖代碼(這段代碼可以去掉瑰排,主要是加了個筆尖贯要,完善功能,提升用戶體驗)
{
//獲取偏移量
//獲取的偏移量是相對于最原始的點
CGPoint transP = [pan translationInView:self.pointView];
[self.pointView setAlpha:1.f];
CGFloat pointW = self.lineWidth + 5;
CGPoint pointP = CGPointMake(curP.x - pointW / 2, curP.y - pointW / 2);
[self.pointView setFrame:(CGRect){pointP, self.lineWidth + 5, self.lineWidth + 5}];
[self.pointView.layer setCornerRadius:self.pointView.bounds.size.width / 2];
[self.pointView.layer setBorderWidth:1.f];
[self.pointView.layer setBorderColor:self.lineColor.CGColor];
self.pointView.transform = CGAffineTransformTranslate(self.pointView.transform, transP.x, transP.y);
//清0操作(不讓偏移量進行累加,獲取的是相對于上一次的值,每一次走的值.)
[pan setTranslation:CGPointMake(0, 0) inView:self.pointView];
}
if (pan.state == UIGestureRecognizerStateBegan) {
ZJBezierPath *path = [ZJBezierPath bezierPath];
path.lineWidth = self.lineWidth;
path.lineJoinStyle = kCGLineJoinRound;
path.lineCapStyle = kCGLineCapRound;
path.lineColor = self.lineColor; // //顏色必須得要在drawRect方法當中進行繪制 繼承系統(tǒng)類,添加屬性我們自己的東西.
self.path = path;
//設置路徑的起點
[self.path moveToPoint:curP];
[self.pathArray addObject:path];
} else if (pan.state == UIGestureRecognizerStateChanged){
//添加一根線到當前手指所在的點
[self.path addLineToPoint:curP];
[self setNeedsDisplay];
} else if (pan.state == UIGestureRecognizerStateEnded){
[self.pointView setAlpha:0.f];
}
}
代碼里都有詳細注釋椭住,下面主要講解一些屬性的設置區(qū)別:
lineJoinStyle:有三種樣式
主要是線條的交接處的樣式
typedef CF_ENUM(int32_t, CGLineJoin) {
kCGLineJoinMiter,
kCGLineJoinRound,
kCGLineJoinBevel
};
注意觀察線條的連接處
kCGLineJoinMiter:
kCGLineJoinRound:
kCGLineJoinBevel
- 實現(xiàn)
- (void)drawRect:(CGRect)rect
方法
- (void)drawRect:(CGRect)rect {
for (ZJBezierPath *path in self.pathArray) {
// 繪制路徑
[path.lineColor set];
[path stroke];
}
我們發(fā)現(xiàn)我們并沒有在該方法內(nèi)部獲取上下文崇渗、描述路徑等操作就可以將路徑繪制到view上去。重點看[path stroke]
方法京郑,其實這個方法內(nèi)部已經(jīng)幫我們實現(xiàn)了繪制視圖的幾個步驟:
- 獲取上下文
- 描述路徑
- 把路徑添加到上下文
- 把上下文的內(nèi)容渲染到View的layer
偽代碼如下:
1. 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
2. 描述路徑
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 10, 10)];
3. 把路徑添加到上下文
CGContextAddPath(ctx, path.CGPath);
4. 把上下文的內(nèi)容渲染到View的layer
CGContextStrokePath(ctx);