iOS中 CoreGraphics快速繪圖(詳解)

第一步:先科普一下基礎(chǔ)知識:

Core Graphics是基于C的API,可以用于一切繪圖操作

Core Graphics 和Quartz 2D的區(qū)別

quartz是一個(gè)通用的術(shù)語,用于描述在IOS和MAC OS X ZHONG 整個(gè)媒體層用到的多種技術(shù) 包括圖形、動畫、音頻、適配。

Quart 2D 是一組二位繪圖和渲染API,Core Graphic會使用到這組API

Quartz Core 專指Core Animation用到的動畫相關(guān)的庫、API和類

點(diǎn)和像素的對比

系統(tǒng)擁有坐標(biāo)系,如320*480 硬件有retain屏幕和非retain屏:如320*480、640*960

Core Graphics 使用的是系統(tǒng)的坐標(biāo)系來繪制圖片。在分辨率為640*960手機(jī)上繪制圖片時(shí),實(shí)際上Core Graphics 的坐標(biāo)是320*480。這個(gè)時(shí)候每個(gè)坐標(biāo)系上的點(diǎn),實(shí)際上擁有兩個(gè)像素。

圖形上下文

Core Graphics 使用圖形上下文進(jìn)行工作,這個(gè)上下文的作用像畫家的畫布一樣。

在圖形上下文之外是無法繪圖的,我們可以自己創(chuàng)建一個(gè)上下文,但是性能和內(nèi)存的使用上,效率是非常低得。

我們可以通過派生一個(gè)UIView的子類,獲得它的上下文。在UIView中調(diào)用drawRect:方法時(shí),會自動準(zhǔn)備好一個(gè)圖形上下文,可以通過調(diào)用

UIGraphicsGetCurrentContext()來獲取。 因?yàn)樗沁\(yùn)行期間繪制圖片,我們可以動態(tài)的做一些額外的操作

Core Graphics的優(yōu)點(diǎn)

快速、高效,減小應(yīng)用的文件大小。同時(shí)可以自由地使用動態(tài)的、高質(zhì)量的圖形圖像。 使用Core Graphics,可以創(chuàng)建直線、路徑、漸變、文字與圖像等內(nèi)容,并可以做變形處理。

繪制自定義視圖

drawRect:是系統(tǒng)的方法,不要從代碼里面直接調(diào)用 drawRect:,而應(yīng)該使用setNeedsDisplay重繪.

需要知道的術(shù)語

路徑 path

陰影 shadow

筆畫 stroke

剪裁路徑 Clip Path

線條粗細(xì) Line Width

混合模式 Blend Mode

填充色 Fill Color

當(dāng)前形變矩陣 Current Transform Matrix

線條圖案 Line Dash

圖形上下文

一個(gè)圖形上下文好比是畫布上的一副扁平的圖畫 執(zhí)行繪畫動作,這些動作是在同一個(gè)圖層上完成的。 圖形上下文不允許將內(nèi)容分不到多個(gè)圖層中,如果有需求在不同圖層上畫,可以考慮使用視圖層次結(jié)構(gòu),創(chuàng)建多個(gè)UIView,并將他們作為父視圖的子視圖

圖形上下文棧可以把圖形上下文的當(dāng)前狀態(tài)保存下來,并在執(zhí)行一些動作后再次恢復(fù)回來

CGContextSaveGState();

CGContextStoreGState();

路徑、漸變、文字和圖像

1. 使用UIBezierPath創(chuàng)建路徑

2. 手動創(chuàng)建路徑 moveToPoint addLineToPoint addArcWithCenter addCurveToPoint

漸變,漸變可以在指定方向上,以可變的比率在一系列顏色之間轉(zhuǎn)化

線性漸變:沿著一條定義好了起點(diǎn)和重點(diǎn)的直線方向,呈線性變化。如果這條線有一定角度,線性漸變也會沿相同路徑變化

放射漸變:顏色順著兩個(gè)原型之間的方向線性變化,這兩個(gè)園為起始圓和終止圓,每隔圓都有自己的圓心和班級

文字

darwAtPoint

drawInRect

圖像

Core Graphics 不會保持圖像的長寬比例,Core Graphics會將圖像的邊界設(shè)置為CGrect,不管圖片是否變形 darwAtPoint drawInRect

第二步:代碼部分:

基礎(chǔ)畫法就不多講啦!都通用:

第一種繪圖形式:在UIView的子類方法drawRect:中繪制一個(gè)藍(lán)色圓,使用UIKit在Cocoa為我們提供的當(dāng)前上下文中完成繪圖任務(wù)。

?- (void) drawRect: (CGRect) rect {

UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];

[[UIColor blueColor] setFill];

[p fill];

}

第二種繪圖形式:使用Core Graphics實(shí)現(xiàn)繪制藍(lán)色圓。

?- (void) drawRect: (CGRect) rect {

CGContextRef con = UIGraphicsGetCurrentContext();

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);

CGContextFillPath(con);

}

第三種繪圖形式:我將在UIView子類的drawLayer:inContext:方法中實(shí)現(xiàn)繪圖任務(wù)。drawLayer:inContext:方法是一個(gè)繪制圖層內(nèi)容的代理方法。為了能夠調(diào)用drawLayer:inContext:方法,我們需要設(shè)定圖層的代理對象。但要注意,不應(yīng)該將UIView對象設(shè)置為顯示層的委托對象,這是因?yàn)閁IView對象已經(jīng)是隱式層的代理對象,再將它設(shè)置為另一個(gè)層的委托對象就會出問題。輕量級的做法是:編寫負(fù)責(zé)繪圖形的代理類。在MyView.h文件中聲明如下代碼:

?@interfaceMyLayerDelegate : NSObject

@end

然后MyView.m文件中實(shí)現(xiàn)接口代碼:

?@implementationMyLayerDelegate

- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx {

UIGraphicsPushContext(ctx);

UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];

[[UIColor blueColor] setFill];

[p fill];

UIGraphicsPopContext();

}

@end

直接將代理類的實(shí)現(xiàn)代碼放在MyView.m文件的#import代碼的下面,這樣感覺好像在使用私有類完成繪圖任務(wù)(雖然這不是私有類)。需要注意的是,我們所引用的上下文并不是當(dāng)前上下文,所以為了能夠使用UIKit,我們需要將引用的上下文轉(zhuǎn)變成當(dāng)前上下文。

因?yàn)閳D層的代理是assign內(nèi)存管理策略,那么這里就不能以局部變量的形式創(chuàng)建MyLayerDelegate實(shí)例對象賦值給圖層代理。這里選擇在MyView.m中增加一個(gè)實(shí)例變量,因?yàn)閷?shí)例變量默認(rèn)是strong:

?@interfaceMyView () {

MyLayerDelegate* _layerDeleagete;

}

@end

使用該圖層代理:

?MyView *myView = [[MyView alloc] initWithFrame: CGRectMake(0,0,320,480)];

CALayer *myLayer = [CALayer layer];

_layerDelegate = [[MyLayerDelegate alloc] init];

myLayer.delegate = _layerDelegate;

[myView.layer addSublayer:myLayer];

[myView setNeedsDisplay];// 調(diào)用此方法,drawLayer: inContext:方法才會被調(diào)用。

第四種繪圖形式:使用Core Graphics在drawLayer:inContext:方法中實(shí)現(xiàn)同樣操作,代碼如下:


?- (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con {

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);

CGContextFillPath(con);

}

最后,演示UIGraphicsBeginImageContextWithOptions的用法,并從上下文中生成一個(gè)UIImage對象。生成UIImage對象的代碼并不需要等待某些方法被調(diào)用后或在UIView的子類中才能去做。

第五種繪圖形式:使用UIKit實(shí)現(xiàn):

?UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO,0);

UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];

[[UIColor blueColor] setFill];

[p fill];

UIImage* im = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

解釋一下UIGraphicsBeginImageContextWithOptions函數(shù)參數(shù)的含義:第一個(gè)參數(shù)表示所要?jiǎng)?chuàng)建的圖片的尺寸;第二個(gè)參數(shù)用來指定所生成圖片的背景是否為不透明,如上我們使用YES而不是NO,則我們得到的圖片背景將會是黑色,顯然這不是我想要的;第三個(gè)參數(shù)指定生成圖片的縮放因子,這個(gè)縮放因子與UIImage的scale屬性所指的含義是一致的。傳入0則表示讓圖片的縮放因子根據(jù)屏幕的分辨率而變化,所以我們得到的圖片不管是在單分辨率還是視網(wǎng)膜屏上看起來都會很好。

第六種繪圖形式:使用Core Graphics實(shí)現(xiàn):

?UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO,0);

CGContextRef con = UIGraphicsGetCurrentContext();

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);

CGContextFillPath(con);

UIImage* im = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

第三步:實(shí)踐部分:

第一種:基本圖形繪制

?/**

*? 什么調(diào)用:當(dāng)你視圖第一次顯示的時(shí)候就會調(diào)用

*? 作用:繪圖

*? @param rect = self.bounds

*/

- (void)drawRect:(CGRect)rect

{

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路徑

UIBezierPath *path = [UIBezierPath bezierPath];

CGPoint startP = CGPointMake(10,125);

CGPoint endP = CGPointMake(240,125);

CGPoint controlP = CGPointMake(125,0);

[path moveToPoint:startP];

[path addQuadCurveToPoint:endP controlPoint:controlP];

// 3.把路徑添加到上下文

CGContextAddPath(ctx, path.CGPath);

// 4.渲染上下文到視圖

CGContextStrokePath(ctx);

}

- (void)drawLine

{

// 1.獲取上下文

// CGContextRef CG CoreGraphics Ref 引用

// 目前學(xué)的上下文都跟UIGraphics有關(guān),以后想直接獲取上下文,直接敲一個(gè)UIGraphics

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.設(shè)置繪圖信息(拼接路徑)

UIBezierPath *path = [UIBezierPath bezierPath];

// 設(shè)置起點(diǎn)

[path moveToPoint:CGPointMake(10,10)];

// 添加一條線到某個(gè)點(diǎn)

[path addLineToPoint:CGPointMake(125,125)];

[path addLineToPoint:CGPointMake(240,10)];

// 3.把路徑添加到上下文

// 直接把UIKit的路徑轉(zhuǎn)換成CoreGraphics,CG開頭就能轉(zhuǎn)

CGContextAddPath(ctx, path.CGPath);

// 4.把上下文渲染到視圖

// Stroke描邊

CGContextStrokePath(ctx);

}

- (void)draw2Line

{

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路徑

UIBezierPath *path = [UIBezierPath bezierPath];

// 設(shè)置起點(diǎn)

[path moveToPoint:CGPointMake(10,125)];

// 添加一條線到某個(gè)點(diǎn)

[path addLineToPoint:CGPointMake(230,125)];

//??? // 設(shè)置起點(diǎn)

//??? [path moveToPoint:CGPointMake(10, 10)];

//

//??? // 添加一條線到某個(gè)點(diǎn)

//??? [path addLineToPoint:CGPointMake(125, 100)];

UIBezierPath *path1 = [UIBezierPath bezierPath];

[path1 moveToPoint:CGPointMake(10,10)];

[path1 addLineToPoint:CGPointMake(125,100)];

// 3.把路徑添加到上下文

CGContextAddPath(ctx, path.CGPath);

CGContextAddPath(ctx, path1.CGPath);

// 設(shè)置繪圖狀態(tài)

// 設(shè)置線寬

CGContextSetLineWidth(ctx,10);

CGContextSetLineCap(ctx, kCGLineCapRound);

//??? CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);

[[UIColor redColor] set];

// 4.渲染上下文到視圖

CGContextStrokePath(ctx);

}

第二種:下載進(jìn)度條:

?- (void)setProgress:(CGFloat)progress

{

_progress = progress;

self.myLabel.text = [NSString stringWithFormat:@"%.2f%%",progress*100];

//??? [self drawRect:self.bounds];

// 重新繪制

// 在view上做一個(gè)重繪的標(biāo)記,當(dāng)下次屏幕刷新的時(shí)候,就會調(diào)用drawRect.

[self setNeedsDisplay];

}

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

// 當(dāng)視圖顯示的時(shí)候會調(diào)用 默認(rèn)只會調(diào)用一次

- (void)drawRect:(CGRect)rect

{

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路徑

CGPoint center = CGPointMake(50,50);

CGFloat radius =50-2;

CGFloat startA = -M_PI_2;

CGFloat endA = -M_PI_2 + _progress * M_PI *2;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

// 3.把路徑添加到上下文

CGContextAddPath(ctx, path.CGPath);

// 4.把上下文渲染到視圖

CGContextStrokePath(ctx);

}

第三種:餅圖

?- (void)drawRect:(CGRect)rect

{

// Drawing code

NSArray *data = @[@25,@25,@50];

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路徑

CGPoint center = CGPointMake(125,125);

CGFloat radius =120;

CGFloat startA =0;

CGFloat angle =0;

CGFloat endA =0;

for(NSNumber *number in data) {

// 2.拼接路徑

startA = endA;

angle = number.intValue /100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path addLineToPoint:center];

[[UIColor randomColor] set];

// 把路徑添加上下文

CGContextAddPath(ctx, path.CGPath);

// 渲染

CGContextFillPath(ctx);

}

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

CGFloat a = arc4random_uniform(6);

//CGFloat a =? arc4random()%6;

NSLog(@"隨機(jī)數(shù)--%f",a);

[self setNeedsDisplay];

}

- (void)drawPie

{

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路徑

CGPoint center = CGPointMake(125,125);

CGFloat radius =120;

CGFloat startA =0;

CGFloat angle =0;

CGFloat endA =0;

// 第一個(gè)扇形

angle =25/100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path addLineToPoint:center];

// 添加到上下文

CGContextAddPath(ctx, path.CGPath);

[[UIColor redColor] set];

// 渲染

CGContextFillPath(ctx);

// 第二個(gè)扇形

startA = endA;

angle =25/100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path1 addLineToPoint:center];

// 添加到上下文

CGContextAddPath(ctx, path1.CGPath);

[[UIColor greenColor] set];

// 渲染

CGContextFillPath(ctx);

// 第三個(gè)扇形

startA = endA;

angle =50/100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path2 addLineToPoint:center];

// 添加到上下文

CGContextAddPath(ctx, path2.CGPath);

[[UIColor blueColor] set];

// 渲染

CGContextFillPath(ctx);

}

第四種:柱形圖

?- (void)drawRect:(CGRect)rect

{

NSArray *data = @[@25,@25,@50];

NSInteger count = data.count;

CGFloat w = rect.size.width / (2* count -1);

CGFloat h =0;

CGFloat x =0;

CGFloat y =0;

CGFloat viewH = rect.size.height;

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

for(inti =0; i < count; i++) {

h = viewH * [data[i] intValue] /100.0;

x =2* w * i;

y = viewH - h;

// 2.拼接路徑

UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, w, h)];

// 3.添加路徑到上下文

CGContextAddPath(ctx, path.CGPath);

[[UIColor randomColor] set];

// 4.渲染

CGContextFillPath(ctx);

}

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

[self setNeedsDisplay];

}

第五種:模仿UIImageView

?- (void)setImage:(UIImage *)image

{

_image = image;

[self setNeedsDisplay];

}

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect

{

// Drawing code

[_image drawInRect:rect];

}


第六種:圖形上下文線

?- (void)drawRect:(CGRect)rect

{

// Drawing code

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 把ctx拷貝一份放在棧中

CGContextSaveGState(ctx);

// 2.拼接路徑(繪圖的信息)

UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(10,125)];

[path addLineToPoint:CGPointMake(240,125)];

// 3.路徑添加到上下文

CGContextAddPath(ctx, path.CGPath);

// 設(shè)置繪圖的狀態(tài)

[[UIColor redColor] set];

CGContextSetLineWidth(ctx,10);

CGContextSetLineCap(ctx, kCGLineCapRound);

// 4.渲染

CGContextStrokePath(ctx);

// 第二根線

UIBezierPath *path1 = [UIBezierPath bezierPath];

[path1 moveToPoint:CGPointMake(125,10)];

[path1 addLineToPoint:CGPointMake(125,240)];

CGContextAddPath(ctx, path1.CGPath);

// 把棧頂上下文取出來,替換當(dāng)前上下文

CGContextRestoreGState(ctx);

// 設(shè)置繪圖的狀態(tài)

//??? [[UIColor blackColor] set];

//??? CGContextSetLineWidth(ctx, 1);

//??? CGContextSetLineCap(ctx, kCGLineCapButt);

// 4.渲染

CGContextStrokePath(ctx);

}

每日更新關(guān)注:https://weibo.com/hanjunqiang新浪微博

第七種:矩形操作

?- (void)drawRect:(CGRect)rect

{

// Drawing code

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 注意:你的路徑一定放在上下文矩陣操作之后

// 平移上下文

CGContextTranslateCTM(ctx,50,100);

// 旋轉(zhuǎn)上下文

CGContextRotateCTM(ctx, M_PI_4);

// 縮放上下文

CGContextScaleCTM(ctx,0.5,1.2);

// 2.拼接路徑

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-50, -100,150,200)];

// 3.把路徑添加到上下文

CGContextAddPath(ctx, path.CGPath);

[[UIColor redColor] set];

// 4.渲染

CGContextFillPath(ctx);

}

詳細(xì):https://www.2cto.com/kf/201608/542831.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Quartz2D以及drawRect的重繪機(jī)制字?jǐn)?shù)1487 閱讀21 評論1 喜歡1一、什么是Quartz2D Q...
    PurpleWind閱讀 916評論 0 3
  • 簡述: 1、Quartz2D是什么Quartz2D是二維繪圖引擎,同時(shí)支持IOS和Mac 2、Quartz2D能做...
    LitterL閱讀 731評論 0 6
  • 什么是Quartz2D 是一個(gè)二維的繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng) Quartz2D的API是純C語言的,它...
    Mario_ZJ閱讀 663評論 0 1
  • 新鮮的! 國慶回家,在XX汽車站前人山人海,在進(jìn)站口排隊(duì)一會兒后,看到進(jìn)站口前有一條橫幅“A, B, C, D, ...
    計(jì)劃逃跑閱讀 397評論 3 1
  • 你不必做什么 我接受 然后我就處于停滯的狀態(tài),講真,要是沒有別的事,我這樣不死不活一輩子都可以。所以說有時(shí)候活著是...
    蹣跚幸福閱讀 311評論 0 0

友情鏈接更多精彩內(nèi)容