iOS中 CoreGraphics快速繪圖(詳解)

第一步:先科普一下基礎知識:

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

Core Graphics 和Quartz 2D的區別

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

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

Quartz Core 專指Core Animation用到的動畫相關的庫、API和類

點和像素的對比

系統擁有坐標系,如320*480 硬件有retain屏幕和非retain屏:如320*480、640*960

Core Graphics 使用的是系統的坐標系來繪制圖片。在分辨率為640*960手機上繪制圖片時,實際上Core Graphics 的坐標是320*480。這個時候每個坐標系上的點,實際上擁有兩個像素。

圖形上下文

Core Graphics 使用圖形上下文進行工作,這個上下文的作用像畫家的畫布一樣。

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

我們可以通過派生一個UIView的子類,獲得它的上下文。在UIView中調用drawRect:方法時,會自動準備好一個圖形上下文,可以通過調用

UIGraphicsGetCurrentContext()來獲取。 因為它是運行期間繪制圖片,我們可以動態的做一些額外的操作

Core Graphics的優點

快速、高效,減小應用的文件大小。同時可以自由地使用動態的、高質量的圖形圖像。 使用Core Graphics,可以創建直線、路徑、漸變、文字與圖像等內容,并可以做變形處理。

繪制自定義視圖

drawRect:是系統的方法,不要從代碼里面直接調用 drawRect:,而應該使用setNeedsDisplay重繪.

需要知道的術語

路徑 path

陰影 shadow

筆畫 stroke

剪裁路徑 Clip Path

線條粗細 Line Width

混合模式 Blend Mode

填充色 Fill Color

當前形變矩陣 Current Transform Matrix

線條圖案 Line Dash

圖形上下文

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

圖形上下文棧可以把圖形上下文的當前狀態保存下來,并在執行一些動作后再次恢復回來

CGContextSaveGState();

CGContextStoreGState();

路徑、漸變、文字和圖像

1. 使用UIBezierPath創建路徑

2. 手動創建路徑 moveToPoint addLineToPoint addArcWithCenter addCurveToPoint

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

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

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

文字

darwAtPoint

drawInRect

圖像

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

第二步:代碼部分:

基礎畫法就不多講啦!都通用:

第一種繪圖形式:在UIView的子類方法drawRect:中繪制一個藍色圓,使用UIKit在Cocoa為我們提供的當前上下文中完成繪圖任務。

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

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

[[UIColor blueColor] setFill];

[p fill];

}

第二種繪圖形式:使用Core Graphics實現繪制藍色圓。

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

CGContextRef con = UIGraphicsGetCurrentContext();

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

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

CGContextFillPath(con);

}

第三種繪圖形式:我將在UIView子類的drawLayer:inContext:方法中實現繪圖任務。drawLayer:inContext:方法是一個繪制圖層內容的代理方法。為了能夠調用drawLayer:inContext:方法,我們需要設定圖層的代理對象。但要注意,不應該將UIView對象設置為顯示層的委托對象,這是因為UIView對象已經是隱式層的代理對象,再將它設置為另一個層的委托對象就會出問題。輕量級的做法是:編寫負責繪圖形的代理類。在MyView.h文件中聲明如下代碼:

?@interfaceMyLayerDelegate : NSObject

@end

然后MyView.m文件中實現接口代碼:

?@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

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

因為圖層的代理是assign內存管理策略,那么這里就不能以局部變量的形式創建MyLayerDelegate實例對象賦值給圖層代理。這里選擇在MyView.m中增加一個實例變量,因為實例變量默認是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];// 調用此方法,drawLayer: inContext:方法才會被調用。

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


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

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

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

CGContextFillPath(con);

}

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

第五種繪圖形式:使用UIKit實現:

?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函數參數的含義:第一個參數表示所要創建的圖片的尺寸;第二個參數用來指定所生成圖片的背景是否為不透明,如上我們使用YES而不是NO,則我們得到的圖片背景將會是黑色,顯然這不是我想要的;第三個參數指定生成圖片的縮放因子,這個縮放因子與UIImage的scale屬性所指的含義是一致的。傳入0則表示讓圖片的縮放因子根據屏幕的分辨率而變化,所以我們得到的圖片不管是在單分辨率還是視網膜屏上看起來都會很好。

第六種繪圖形式:使用Core Graphics實現:

?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();

第三步:實踐部分:

第一種:基本圖形繪制

?/**

*? 什么調用:當你視圖第一次顯示的時候就會調用

*? 作用:繪圖

*? @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 引用

// 目前學的上下文都跟UIGraphics有關,以后想直接獲取上下文,直接敲一個UIGraphics

CGContextRef ctx = UIGraphicsGetCurrentContext();

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

UIBezierPath *path = [UIBezierPath bezierPath];

// 設置起點

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

// 添加一條線到某個點

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

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

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

// 直接把UIKit的路徑轉換成CoreGraphics,CG開頭就能轉

CGContextAddPath(ctx, path.CGPath);

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

// Stroke描邊

CGContextStrokePath(ctx);

}

- (void)draw2Line

{

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路徑

UIBezierPath *path = [UIBezierPath bezierPath];

// 設置起點

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

// 添加一條線到某個點

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

//??? // 設置起點

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

//

//??? // 添加一條線到某個點

//??? [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);

// 設置繪圖狀態

// 設置線寬

CGContextSetLineWidth(ctx,10);

CGContextSetLineCap(ctx, kCGLineCapRound);

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

[[UIColor redColor] set];

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

CGContextStrokePath(ctx);

}

第二種:下載進度條:

?- (void)setProgress:(CGFloat)progress

{

_progress = progress;

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

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

// 重新繪制

// 在view上做一個重繪的標記,當下次屏幕刷新的時候,就會調用drawRect.

[self setNeedsDisplay];

}

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

// An empty implementation adversely affects performance during animation.

// 當視圖顯示的時候會調用 默認只會調用一次

- (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(@"隨機數--%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;

// 第一個扇形

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);

// 第二個扇形

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);

// 第三個扇形

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);

// 設置繪圖的狀態

[[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);

// 把棧頂上下文取出來,替換當前上下文

CGContextRestoreGState(ctx);

// 設置繪圖的狀態

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

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

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

// 4.渲染

CGContextStrokePath(ctx);

}

每日更新關注:https://weibo.com/hanjunqiang新浪微博

第七種:矩形操作

?- (void)drawRect:(CGRect)rect

{

// Drawing code

// 1.獲取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

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

// 平移上下文

CGContextTranslateCTM(ctx,50,100);

// 旋轉上下文

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);

}

詳細:https://www.2cto.com/kf/201608/542831.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容

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