原文出處: KenshinCui html
概述html5
在iOS中能夠很容易的開發出絢麗的界面效果,一方面得益於成功系統的設計,另外一方面得益於它強大的開發框架。今天咱們將圍繞iOS中兩大圖形、圖像繪圖框架進行介紹:Quartz 2D繪製2D圖形和Core Image中強大的濾鏡功能。程序員
在iOS中經常使用的繪圖框架就是Quartz 2D,Quartz 2D是Core Graphics框架的一部分,是一個強大的二維圖像繪製引擎。Quartz 2D在UIKit中也有很好的封裝和集成,咱們平常開發時所用到的UIKit中的組件都是由Core Graphics進行繪製的。不只如此,當咱們引入UIKit框架時系統會自動引入Core Graphics框架,而且爲了方便開發者使用在UIKit內部還對一些經常使用的繪圖API進行了封裝。算法
在iOS中繪圖通常分爲如下幾個步驟:編程
1.獲取繪圖上下文canvas
2.建立並設置路徑數組
3.將路徑添加到上下文架構
4.設置上下文狀態app
5.繪製路徑框架
6.釋放路徑
圖形上下文CGContextRef表明圖形輸出設備(也就是繪製的位置),包含了繪製圖形的一些設備信息,Quartz 2D中的全部對象最終都必須繪製到圖形上下文。這樣一來,咱們在繪製圖形時就沒必要關心具體的設備信息,統一了代碼編寫方式(在Quartz 2D中的繪圖上下文能夠是位圖Bitmap、PDF、窗口Window、層Layer、打印對象Printer)。
在UIKit中默認已經爲咱們準備好了一個圖形上下文對象,在UI控件的drawRect:方法(這個方法在loadView、viewDidLoad方法後執行)中咱們能夠經過UIKit封裝函數UIGraphicsGetCurrentContext()方法得到這個圖形上下文(注意在其餘UI控件方法中沒法取得這個對象),而後咱們只要按照繪圖步驟一步步執行便可。下面自定義一個KCView繼承自UIView,重寫drawRect:方法繪製兩條直線說明上面繪圖的步驟:
KCView.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
// // KCView.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // /** 基本繪圖 */
#import "KCView.h"
@implementation KCView
#pragma mark 繪圖 //繪圖只能在此方法中調用,不然沒法獲得當前圖形上下文 -(void)drawRect:(CGRect)rect{ //1.取得圖形上下文對象 CGContextRef context = UIGraphicsGetCurrentContext();
//2.建立路徑對象 CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, nil, 20, 50);//移動到指定位置(設置路徑起點) CGPathAddLineToPoint(path, nil, 20, 100);//繪製直線(從起始位置開始) CGPathAddLineToPoint(path, nil, 300, 100);//繪製另一條直線(從上一直線終點開始繪製)
//3.添加路徑到圖形上下文 CGContextAddPath(context, path);
//4.設置圖形上下文狀態屬性 CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);//設置筆觸顏色 CGContextSetRGBFillColor(context, 0, 1.0, 0, 1);//設置填充色 CGContextSetLineWidth(context, 2.0);//設置線條寬度 CGContextSetLineCap(context, kCGLineCapRound);//設置頂點樣式,(20,50)和(300,100)是頂點 CGContextSetLineJoin(context, kCGLineJoinRound);//設置鏈接點樣式,(20,100)是鏈接點 /*設置線段樣式 phase:虛線開始的位置 lengths:虛線長度間隔(例以下面的定義說明第一條線段長度8,而後間隔3從新繪製8點的長度線段,固然這個數組能夠定義更多元素) count:虛線數組元素個數 */ CGFloat lengths[2] = { 18, 9 }; CGContextSetLineDash(context, 0, lengths, 2); /*設置陰影 context:圖形上下文 offset:偏移量 blur:模糊度 color:陰影顏色 */ CGColorRef color = [UIColor grayColor].CGColor;//顏色轉化,因爲Quartz 2D跨平臺,因此其中不能使用UIKit中的對象,可是UIkit提供了轉化方法 CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 0.8, color);
//5.繪製圖像到指定圖形上下文 /*CGPathDrawingMode是填充方式,枚舉類型 kCGPathFill:只有填充(非零纏繞數填充),不繪製邊框 kCGPathEOFill:奇偶規則填充(多條路徑交叉時,奇數交叉填充,偶交叉不填充) kCGPathStroke:只有邊框 kCGPathFillStroke:既有邊框又有填充 kCGPathEOFillStroke:奇偶填充並繪製邊框 */ CGContextDrawPath(context, kCGPathFillStroke);//最後一個參數是填充類型
//6.釋放對象 CGPathRelease(path); } @end |
在視圖控制器建立KCView並添加到根視圖中:
1 2 3 4 5 6 7 |
- (void)viewDidLoad { [super viewDidLoad];
KCView *view=[[KCView alloc]initWithFrame:[UIScreen mainScreen].bounds]; view.backgroundColor=[UIColor whiteColor]; [self.view addSubview:view]; } |
運行效果以下:
上面的繪圖方式未免顯得有些麻煩,其實Core Graphics 內部對建立對象添加到上下文這兩步操做進行了封裝,能夠一步完成。另外前面也說過UIKit內部其實封裝了一些以「UI」開頭的方法幫助你們進行圖形繪製。就拿前面的例子來講咱們改進一些繪製方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
-(void)drawLine2{ //1.得到圖形上下文 CGContextRef context=UIGraphicsGetCurrentContext();
//2.繪製路徑(至關於前面建立路徑並添加路徑到圖形上下文兩步操做) CGContextMoveToPoint(context, 20, 50); CGContextAddLineToPoint(context, 20, 100); CGContextAddLineToPoint(context, 300, 100); //封閉路徑:a.建立一條起點和終點的線,不推薦 //CGPathAddLineToPoint(path, nil, 20, 50); //封閉路徑:b.直接調用路徑封閉方法 CGContextClosePath(context);
//3.設置圖形上下文屬性 [[UIColor redColor]setStroke];//設置紅色邊框 [[UIColor greenColor]setFill];//設置綠色填充 //[[UIColor blueColor]set];//同時設置填充和邊框色
//4.繪製路徑 CGContextDrawPath(context, kCGPathFillStroke); } |
上面的操做相比前面的方法應該說已經簡化了很多,除了路徑以外其餘矩形、橢圓等都有對應的建立方法。另外上面咱們也演示了封閉路徑的方法,你們能夠運行看一下效果。
相信你們瞭解了上面的繪製步驟其餘圖形繪製並不麻煩,下面以一個例子簡單演示一下其餘圖形的繪製,包括文字和圖像的繪製。
在下面的方法中還能夠看到UIKit對繪圖方法的封裝,使用起來更加簡單。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#pragma mark 繪製矩形 -(void)drawRectWithContext:(CGContextRef)context{ //添加矩形對象 CGRect rect=CGRectMake(20, 50, 280.0, 50.0); CGContextAddRect(context,rect); //設置屬性 [[UIColor blueColor]set]; //繪製 CGContextDrawPath(context, kCGPathFillStroke); }
#pragma mark 繪製矩形(利用UIKit的封裝方法) -(void)drawRectByUIKitWithContext:(CGContextRef)context{ CGRect rect= CGRectMake(20, 150, 280.0, 50.0); CGRect rect2=CGRectMake(20, 250, 280.0, 50.0); //設置屬性 [[UIColor yellowColor]set]; //繪製矩形,至關於建立對象、添加對象到上下文、繪製三個步驟 UIRectFill(rect);//繪製矩形(只有填充)
[[UIColor redColor]setStroke]; UIRectFrame(rect2);//繪製矩形(只有邊框) } @end |
運行效果:
1 2 3 4 5 6 7 8 9 10 11 |
#pragma mark 繪製橢圓 -(void)drawEllipse:(CGContextRef)context{ //添加對象,繪製橢圓(圓形)的過程也是先建立一個矩形 CGRect rect=CGRectMake(50, 50, 220.0, 200.0); CGContextAddEllipseInRect(context, rect); //設置屬性 [[UIColor purpleColor]set]; //繪製 CGContextDrawPath(context, kCGPathFillStroke); } @end |
運行效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
-(void)drawArc:(CGContextRef)context{ /*添加弧形對象 x:中心點x座標 y:中心點y座標 radius:半徑 startAngle:起始弧度 endAngle:終止弧度 closewise:是否逆時針繪製,0則順時針繪製 */ CGContextAddArc(context, 160, 160, 100.0, 0.0, M_PI_2, 1);
//設置屬性 [[UIColor yellowColor]set];
//繪製 CGContextDrawPath(context, kCGPathFillStroke); } |
運行效果:
要繪製規則圖形在iOS中至關簡單,可是不規則圖形怎麼繪製呢?此時就要利用路徑。前面咱們繪製了直線,它和曲線繪製都屬於路徑繪製。和直線繪製相比曲線繪製就要複雜一些,可是路徑做爲高級動畫的基礎又是咱們必須掌握的,所以這裏咱們就一塊兒來熟悉一下曲線繪製。在Quartz 2D中曲線繪製分爲兩種:二次貝塞爾曲線和三次貝塞爾曲線。二次曲線只有一個控制點,而三次曲線有兩個控制點,以下圖所示:
固然,在iOS中兩種曲線分別對應兩種方法:
CGContextAddQuadCurveToPoint(CGContextRef c, CGFloat cpx, CGFloat cpy, CGFloat x, CGFloat y);
CGContextAddCurveToPoint(context, CGFloat cp1x, CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y);
下面就演示一下這兩種曲線的繪製方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#pragma mark 繪製貝塞爾曲線 -(void)drawCurve:(CGContextRef)context{
//繪製曲線 CGContextMoveToPoint(context, 20, 100);//移動到起始位置 /*繪製二次貝塞爾曲線 c:圖形上下文 cpx:控制點x座標 cpy:控制點y座標 x:結束點x座標 y:結束點y座標 */ CGContextAddQuadCurveToPoint(context, 160, 0, 300, 100);
CGContextMoveToPoint(context, 20, 500); /*繪製三次貝塞爾曲線 c:圖形上下文 cp1x:第一個控制點x座標 cp1y:第一個控制點y座標 cp2x:第二個控制點x座標 cp2y:第二個控制點y座標 x:結束點x座標 y:結束點y座標 */ CGContextAddCurveToPoint(context, 80, 300, 240, 500, 300, 300);
//設置圖形上下文屬性 [[UIColor yellowColor]setFill]; [[UIColor redColor]setStroke];
//繪製路徑 CGContextDrawPath(context, kCGPathFillStroke);
} |
運行效果:
備註:貝塞爾曲線是由法國數學家「貝塞爾」發現的,他發現:任何一條曲線都可以由和它相切的直線的兩個端點來描述,這種曲線表示方式後來被普遍應用到計算機中,稱爲「貝塞爾曲線」。
除了繪製圖形還能夠繪製文本內容。
1 2 3 4 5 6 7 8 9 10 11 |
-(void)drawText:(CGContextRef)context{ //繪製到指定的區域內容 NSString *str=@"Star Walk is the most beautiful stargazing app you’ve ever seen on a mobile device. It will become your go-to interactive astro guide to the night sky, following your every movement in real-time and allowing you to explore over 200, 000 celestial bodies with extensive information about stars and constellations that you find."; CGRect rect= CGRectMake(20, 50, 280, 300); UIFont *font=[UIFont systemFontOfSize:18];//設置字體 UIColor *color=[UIColor redColor];//字體顏色 NSMutableParagraphStyle *style=[[NSMutableParagraphStyle alloc]init];//段落樣式 NSTextAlignment align=NSTextAlignmentLeft;//對齊方式 style.alignment=align; [str drawInRect:rect withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style}]; } |
運行效果:
Quartz 2D還能夠將圖像繪製到圖形上下文。
1 2 3 4 5 6 7 8 9 |
-(void)drawImage:(CGContextRef)context{ UIImage *image=[UIImage imageNamed:@"image2.jpg"]; //從某一點開始繪製 [image drawAtPoint:CGPointMake(10, 50)]; //繪製到指定的矩形中,注意若是大小不合適會會進行拉伸 // [image drawInRect:CGRectMake(10, 50, 300, 450)]; //平鋪繪製 // [image drawAsPatternInRect:CGRectMake(0, 0, 320, 568)]; } |
運行效果:
從前面的示例中咱們能夠看到如何設置填充顏色,事實上不少時候純色的填充並不能知足咱們的需求,例若有時候咱們要繪製一些圖形可能須要設置一個漂亮的背景,這個時候咱們可能就會選擇漸變填充方式。Quartz 2D的漸變方式分爲兩種:
a.線性漸變線:漸變色以直線方式從開始位置逐漸向結束位置漸變
b.徑向漸變:以中心點爲圓心從起始漸變色向四周輻射,直到終止漸變色
要作漸變則必須先設置從開始位置到結束位置的漸變顏色,作過photoshop的朋友相信對於漸變色設置並不陌生,只要在指定位置指定不一樣的顏色,剩下的事情交給系統處理便可,以下圖在起始位置、3/10位置、結束位置指定了三種顏色就造成由三種顏色組成的漸變色:
另外,在iOS中繪製漸變還須要注意一點就是指定顏色空間,所謂顏色空間就是不一樣顏色在不一樣的維度上取值最終組成一種顏色的過程。就拿RGB來講,若是將紅色、綠色、藍色當作是x、y、z軸座標系,那麼在三個座標上分別取0~255範圍內的不一樣值則能夠組成各種顏色。固然,不一樣顏色空間的「座標系」也是不一樣的(也就是說顏色表示的方式是不一樣的),經常使用的顏色空間除了RGB還有CMYK(印刷業經常使用這種顏色模式)、Gray。
在使用Quartz 2D繪圖時咱們的顏色除了使用常規的方法(如何前面CGContextSetRGBFillColor(CGContextRef context, CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)方法)設置RGB和透明度外,有時還會遇到顏色參數是一個數組狀況。如使用顏色空間填充時用到的CGContextSetFillColor(CGContextRef context, const CGFloat *components)方法,這個時候components數組中具體是如何存儲顏色就要根據顏色空間而定,若是顏色空間使用RGB則數組中的元素四個爲一組,分別是red(紅)、green(綠)、blue(藍)、alpha(透明度);若是使用CMYK顏色空間,那麼數組中的元素五個爲一組,分別是cyan(青)、magenta(洋紅)、yellow(黃)、black(黑)、alpha(透明度)。
下面的代碼分別演示了兩種漸變方式,具體漸變繪製函數參數代碼中已經註釋的很清楚了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
// // KCView3.m // Quartz2D // // Created by Kenshin Cui on 14-3-16. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. //
#import "KCView3.h"
@implementation KCView3
-(void)drawRect:(CGRect)rect{ CGContextRef context=UIGraphicsGetCurrentContext(); // [self drawLinearGradient:context]; [self drawRadialGradient:context]; }
#pragma mark 線性漸變 -(void)drawLinearGradient:(CGContextRef)context{ //使用rgb顏色空間 CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
/*指定漸變色 space:顏色空間 components:顏色數組,注意因爲指定了RGB顏色空間,那麼四個數組元素表示一個顏色(red、green、blue、alpha), 若是有三個顏色則這個數組有4*3個元素 locations:顏色所在位置(範圍0~1),這個數組的個數不小於components中存放顏色的個數 count:漸變個數,等於locations的個數 */ CGFloat compoents[12]={ 248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0 }; CGFloat locations[3]={0,0.3,1.0}; CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);
/*繪製線性漸變 context:圖形上下文 gradient:漸變色 startPoint:起始位置 endPoint:終止位置 options:繪製方式,kCGGradientDrawsBeforeStartLocation 開始位置以前就進行繪製,到結束位置以後再也不繪製, kCGGradientDrawsAfterEndLocation開始位置以前不進行繪製,到結束點以後繼續填充 */ CGContextDrawLinearGradient(context, gradient, CGPointZero, CGPointMake(320, 300), kCGGradientDrawsAfterEndLocation);
//釋放顏色空間 CGColorSpaceRelease(colorSpace); }
#pragma mark 徑向漸變 -(void)drawRadialGradient:(CGContextRef)context{ //使用rgb顏色空間 CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
/*指定漸變色 space:顏色空間 components:顏色數組,注意因爲指定了RGB顏色空間,那麼四個數組元素表示一個顏色(red、green、blue、alpha), 若是有三個顏色則這個數組有4*3個元素 locations:顏色所在位置(範圍0~1),這個數組的個數不小於components中存放顏色的個數 count:漸變個數,等於locations的個數 */ CGFloat compoents[12]={ 248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0 }; CGFloat locations[3]={0,0.3,1.0}; CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);
/*繪製徑向漸變 context:圖形上下文 gradient:漸變色 startCenter:起始點位置 startRadius:起始半徑(一般爲0,不然在此半徑範圍內容無任何填充) endCenter:終點位置(一般和起始點相同,不然會有偏移) endRadius:終點半徑(也就是漸變的擴散長度) options:繪製方式,kCGGradientDrawsBeforeStartLocation 開始位置以前就進行繪製,可是到結束位置以後再也不繪製, kCGGradientDrawsAfterEndLocation開始位置以前不進行繪製,但到結束點以後繼續填充 */ CGContextDrawRadialGradient(context, gradient, CGPointMake(160, 284),0, CGPointMake(165, 289), 150, kCGGradientDrawsAfterEndLocation); //釋放顏色空間 CGColorSpaceRelease(colorSpace); } @end |
運行效果:
上面咱們只是繪製漸變到圖形上下文,實際開發中有時候咱們還須要填充對應的漸變色,例如如今繪製了一個矩形,如何填充成漸變色呢?在此能夠利用漸變裁切來完成(固然利用層CALayer更加方便但這不在今天的話題討論範圍內),特別說明一下區域裁切並不只僅適用於漸變填充,對於其餘圖形繪製仍然適用,而且注意裁切只能限於矩形裁切。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
-(void)drawRectWithLinearGradientFill:(CGContextRef)context{ CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
//裁切處一塊矩形用於顯示,注意必須先裁切再調用漸變 //CGContextClipToRect(context, CGRectMake(20, 50, 280, 300)); //裁切還可使用UIKit中對應的方法 UIRectClip(CGRectMake(20, 50, 280, 300));
CGFloat compoents[12]={ 248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0 }; CGFloat locations[3]={0,0.3,1.0}; CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);
CGContextDrawLinearGradient(context, gradient, CGPointMake(20, 50), CGPointMake(300, 300), kCGGradientDrawsAfterEndLocation);
//釋放顏色空間 CGColorSpaceRelease(colorSpace); } |
運行效果:
經常使用的圖形上下文狀態設置上面基本都用到了,咱們再也不一一解釋,這裏着重說一下疊加模式和填充模式,初學者對於這兩個狀態設置每每容易產生疑惑。
使用Quartz 2D繪圖時後面繪製的圖像會覆蓋前面的,默認狀況下若是前面的被覆蓋後將看不到後面的內容,可是有時候這個結果並非咱們想要的,所以在Quartz 2D中提供了填充模式供開發者配置調整。因爲填充模式類別特別多,所以下面以一個例子來講明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
-(void)drawRectByUIKitWithContext2:(CGContextRef)context{ CGRect rect= CGRectMake(0, 130.0, 320.0, 50.0); CGRect rect1= CGRectMake(0, 390.0, 320.0, 50.0);
CGRect rect2=CGRectMake(20, 50.0, 10.0, 250.0); CGRect rect3=CGRectMake(40.0, 50.0, 10.0, 250.0); CGRect rect4=CGRectMake(60.0, 50.0, 10.0, 250.0); CGRect rect5=CGRectMake(80.0, 50.0, 10.0, 250.0); CGRect rect6=CGRectMake(100.0, 50.0, 10.0, 250.0); CGRect rect7=CGRectMake(120.0, 50.0, 10.0, 250.0); CGRect rect8=CGRectMake(140.0, 50.0, 10.0, 250.0); CGRect rect9=CGRectMake(160.0, 50.0, 10.0, 250.0); CGRect rect10=CGRectMake(180.0, 50.0, 10.0, 250.0); CGRect rect11=CGRectMake(200.0, 50.0, 10.0, 250.0); CGRect rect12=CGRectMake(220.0, 50.0, 10.0, 250.0); CGRect rect13=CGRectMake(240.0, 50.0, 10.0, 250.0); CGRect rect14=CGRectMake(260.0, 50.0, 10.0, 250.0); CGRect rect15=CGRectMake(280.0, 50.0, 10.0, 250.0);
CGRect rect16=CGRectMake(30.0, 310.0, 10.0, 250.0); CGRect rect17=CGRectMake(50.0, 310.0, 10.0, 250.0); CGRect rect18=CGRectMake(70.0, 310.0, 10.0, 250.0); CGRect rect19=CGRectMake(90.0, 310.0, 10.0, 250.0); CGRect rect20=CGRectMake(110.0, 310.0, 10.0, 250.0); CGRect rect21=CGRectMake(130.0, 310.0, 10.0, 250.0); CGRect rect22=CGRectMake(150.0, 310.0, 10.0, 250.0); CGRect rect23=CGRectMake(170.0, 310.0, 10.0, 250.0); CGRect rect24=CGRectMake(190.0, 310.0, 10.0, 250.0); CGRect rect25=CGRectMake(210.0, 310.0, 10.0, 250.0); CGRect rect26=CGRectMake(230.0, 310.0, 10.0, 250.0); CGRect rect27=CGRectMake(250.0, 310.0, 10.0, 250.0); CGRect rect28=CGRectMake(270.0, 310.0, 10.0, 250.0); CGRect rect29=CGRectMake(290.0, 310.0, 10.0, 250.0);
[[UIColor yellowColor]set]; UIRectFill(rect);
[[UIColor greenColor]setFill]; UIRectFill(rect1);
[[UIColor redColor]setFill]; UIRectFillUsingBlendMode(rect2, kCGBlendModeClear); UIRectFillUsingBlendMode(rect3, kCGBlendModeColor); UIRectFillUsingBlendMode(rect4, kCGBlendModeColorBurn); UIRectFillUsingBlendMode(rect5, kCGBlendModeColorDodge); UIRectFillUsingBlendMode(rect6, kCGBlendModeCopy); UIRectFillUsingBlendMode(rect7, kCGBlendModeDarken); UIRectFillUsingBlendMode(rect8, kCGBlendModeDestinationAtop); UIRectFillUsingBlendMode(rect9, kCGBlendModeDestinationIn); UIRectFillUsingBlendMode(rect10, kCGBlendModeDestinationOut); UIRectFillUsingBlendMode(rect11, kCGBlendModeDestinationOver); UIRectFillUsingBlendMode(rect12, kCGBlendModeDifference); UIRectFillUsingBlendMode(rect13, kCGBlendModeExclusion); UIRectFillUsingBlendMode(rect14, kCGBlendModeHardLight); UIRectFillUsingBlendMode(rect15, kCGBlendModeHue); UIRectFillUsingBlendMode(rect16, kCGBlendModeLighten);
UIRectFillUsingBlendMode(rect17, kCGBlendModeLuminosity); UIRectFillUsingBlendMode(rect18, kCGBlendModeMultiply); UIRectFillUsingBlendMode(rect19, kCGBlendModeNormal); UIRectFillUsingBlendMode(rect20, kCGBlendModeOverlay); UIRectFillUsingBlendMode(rect21, kCGBlendModePlusDarker); UIRectFillUsingBlendMode(rect22, kCGBlendModePlusLighter); UIRectFillUsingBlendMode(rect23, kCGBlendModeSaturation); UIRectFillUsingBlendMode(rect24, kCGBlendModeScreen); UIRectFillUsingBlendMode(rect25, kCGBlendModeSoftLight); UIRectFillUsingBlendMode(rect26, kCGBlendModeSourceAtop); UIRectFillUsingBlendMode(rect27, kCGBlendModeSourceIn); UIRectFillUsingBlendMode(rect28, kCGBlendModeSourceOut); UIRectFillUsingBlendMode(rect29, kCGBlendModeXOR); } |
運行效果:
相信你們對比代碼和顯示效果並不難發現每種疊加的效果。例子中只是使用UIKit的封裝方法進行疊加模式設置,更通常的方法固然是使用CGContextSetBlendMode(CGContextRef context, CGBlendMode mode)方法進行設置。
前面的示例中已經演示過純色填充、漸變填充,而有時咱們須要按必定的自定義樣式進行填充,這種方式有點相似於貼瓷磚的方式。咱們知道若是家裏貼地板或瓷磚時,一般咱們會先選擇一種瓷磚樣式,根據房間面積咱們購買不一樣量的瓷磚。可是無論買多少,這些瓷磚的樣式都是如出一轍的。填充模式就是爲了達到這種效果而產生的:咱們只須要繪製一個瓷磚的樣式,而後讓程序自動調用這種樣式填充指定大小的區域。
Quartz 2D支持兩種填充模式:有顏色填充和無顏色填充。兩種模式使用起來區別很小,有顏色填充就是在繪製瓷磚時就指定顏色,在調用填充時就不用再指定瓷磚顏色;無顏色填充模式就是繪製瓷磚時不用指定任何顏色,在調用填充時再指定具體填充顏色。相比較無顏色填充模式而言,有顏色填充模式更加的靈活,推薦使用。
下面咱們具體看一下如何按指定模式進行圖形填充:
1.在使用填充模式時首先要構建一個符合CGPatternDrawPatternCallback簽名的方法,這個方法專門用來建立「瓷磚」。注意:若是使用有顏色填充模式,須要設置填充色。例如咱們定義一個方法drawTile繪製如下瓷磚(有顏色填充):
2.接着須要指定一個填充的顏色空間,這個顏色空間跟前面繪製漸變的顏色空間不太同樣,前面建立漸變使用的顏色空間是設備無關的,咱們須要基於這個顏色空間建立一個顏色空間專門用於填充(注意對於有顏色填充建立填充顏色空間參數爲NULL,不用基於設備無關的顏色空間建立)。
3.而後咱們就可使用CGPatternCreate方法建立一個填充模式,建立填充模式時須要注意其中的參數,在代碼中已經作了一一解釋(這裏注意對於有顏色填充模式isColored設置爲true,不然爲false)。
4.最後調用CGContextSetFillPattern方法給圖形上下文指定填充模式(這個時候注意最後一個參數,若是是有顏色填充模式最後一個參數爲透明度alpa的地址,對於無顏色填充模式最後一個參數是當前填充顏色空間的顏色數組)。
5.繪製圖形,這裏咱們繪製一個矩形。
6.釋放資源。
下面是具體代碼(包含兩種填充模式代碼,能夠一一運行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
// // UIView4.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. //
#import "KCView.h" #define TILE_SIZE 20
@implementation KCView
-(void)drawRect:(CGRect)rect{ CGContextRef context=UIGraphicsGetCurrentContext(); [self drawBackgroundWithColoredPattern:context]; // [self drawBackgroundWithPattern:context];
}
#pragma mark - 有顏色填充模式 void drawColoredTile(void *info,CGContextRef context){ //有顏色填充,這裏設置填充色 CGContextSetRGBFillColor(context, 254.0/255.0, 52.0/255.0, 90.0/255.0, 1); CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE)); CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)); } -(void)drawBackgroundWithColoredPattern:(CGContextRef)context{ //設備無關的顏色空間 // CGColorSpaceRef rgbSpace= CGColorSpaceCreateDeviceRGB(); //模式填充顏色空間,注意對於有顏色填充模式,這裏傳NULL CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(NULL); //將填充色顏色空間設置爲模式填充的顏色空間 CGContextSetFillColorSpace(context, colorSpace);
//填充模式回調函數結構體 CGPatternCallbacks callback={0,&drawColoredTile,NULL}; /*填充模式 info://傳遞給callback的參數 bounds:瓷磚大小 matrix:形變 xStep:瓷磚橫向間距 yStep:瓷磚縱向間距 tiling:貼磚的方法 isClored:繪製的瓷磚是否已經指定了顏色(對於有顏色瓷磚此處指定位true) callbacks:回調函數 */ CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity,2*TILE_SIZE+ 5,2*TILE_SIZE+ 5, kCGPatternTilingNoDistortion, true, &callback);
CGFloat alpha=1; //注意最後一個參數對於有顏色瓷磚指定爲透明度的參數地址,對於無顏色瓷磚則指定當前顏色空間對應的顏色數組 CGContextSetFillPattern(context, pattern, &alpha);
UIRectFill(CGRectMake(0, 0, 320, 568));
// CGColorSpaceRelease(rgbSpace); CGColorSpaceRelease(colorSpace); CGPatternRelease(pattern); }
#pragma mark - 無顏色填充模式 //填充瓷磚的回調函數(必須知足CGPatternCallbacks簽名) void drawTile(void *info,CGContextRef context){ CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE)); CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)); } -(void)drawBackgroundWithPattern:(CGContextRef)context{ //設備無關的顏色空間 CGColorSpaceRef rgbSpace= CGColorSpaceCreateDeviceRGB(); //模式填充顏色空間 CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(rgbSpace); //將填充色顏色空間設置爲模式填充的顏色空間 CGContextSetFillColorSpace(context, colorSpace);
//填充模式回調函數結構體 CGPatternCallbacks callback={0,&drawTile,NULL}; /*填充模式 info://傳遞給callback的參數 bounds:瓷磚大小 matrix:形變 xStep:瓷磚橫向間距 yStep:瓷磚縱向間距 tiling:貼磚的方法(瓷磚擺放的方式) isClored:繪製的瓷磚是否已經指定了顏色(對於無顏色瓷磚此處指定位false) callbacks:回調函數 */ CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity,2*TILE_SIZE+ 5,2*TILE_SIZE+ 5, kCGPatternTilingNoDistortion, false, &callback);
CGFloat components[]={254.0/255.0,52.0/255.0,90.0/255.0,1.0}; //注意最後一個參數對於無顏色填充模式指定爲當前顏色空間顏色數據 CGContextSetFillPattern(context, pattern, components); // CGContextSetStrokePattern(context, pattern, components); UIRectFill(CGRectMake(0, 0, 320, 568));
CGColorSpaceRelease(rgbSpace); CGColorSpaceRelease(colorSpace); CGPatternRelease(pattern); } @end |
運行效果:
這裏強調一點,在drawTile回調方法中不要使用UIKit封裝方法進行圖形繪製(例如UIRectFill等),因爲這個方法由Core Graphics內部調用,而Core Graphics考慮到跨平臺問題,內部是不容許調用UIKit方法的。
咱們知道在UIKit開發中UIView有一個transform屬性用於控件的形變,其實在繪圖中咱們也常常用到圖形形變,這個時候能夠藉助圖形上下文的形變方法來完成。在弄清形變以前咱們要清楚圖形上下文的座標原點,由於不管是位移仍是旋轉都是相對於座標原點進行的。其實Quartz 2D的座標系同UIKit並不同,它的座標原點在屏幕左下方,可是爲了統一編程方式,UIKit對其進行了轉換,座標原點統一在屏幕左上角。注意在設置圖形上下文形變以前必定要注意保存上下文的初始狀態,在使用完以後進行恢復。不然在處理多個圖形形變的時候很容易弄不清楚究竟是基於怎樣的座標系進行繪圖,容易找不到原點(作過html5 canvas繪圖的朋友對這一點應該很熟悉,在html5中繪圖也常常進行狀態保存和恢復)。下面經過一個圖片的變換演示一下圖形上下文的形變(其餘圖形也是同樣的,就再也不演示):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// // KCView2.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. //
#import "KCView2.h"
@implementation KCView2
-(void)drawRect:(CGRect)rect{ CGContextRef context=UIGraphicsGetCurrentContext(); [self drawImage:context]; }
#pragma mark 圖形上下文形變 -(void)drawImage:(CGContextRef)context{ //保存初始狀態 CGContextSaveGState(context);
//形變第一步:圖形上下文向右平移40 CGContextTranslateCTM(context, 100, 0);
//形變第二步:縮放0.8 CGContextScaleCTM(context, 0.8, 0.8);
//形變第三步:旋轉 CGContextRotateCTM(context, M_PI_4/4);
UIImage *image=[UIImage imageNamed:@"photo1.jpg"]; [image drawInRect:CGRectMake(0, 50, 240, 300)];
//恢復到初始狀態 CGContextRestoreGState(context); }
@end |
最終運行效果見第四幅截圖,下圖描繪出了整個程序的運行過程(移動->縮放->旋轉):
在前面基本繪圖部分,繪製圖像時使用了UIKit中封裝的方法進行了圖像繪製,咱們不妨看一下使用Quartz 2D內置方法繪製是什麼效果。
1 2 3 4 5 6 |
-(void)drawImage2:(CGContextRef)context{ UIImage *image=[UIImage imageNamed:@"image2.jpg"]; //圖像繪製 CGRect rect= CGRectMake(10, 50, 300, 450); CGContextDrawImage(context, rect, image.CGImage); } |
運行效果:
看起來整個圖像是倒過來的,緣由正是前面說的:在Core Graphics中座標系的y軸正方向是向上的,座標原點在屏幕左下角,y軸方向恰好和UIKit中y軸方向相反。而使用UIKit進行繪圖之因此沒有問題是由於UIKit中進行了處理,事實上對於其餘圖形即便使用Core Graphics繪製也沒有問題,由於UIKit統一了編程方式。可是使用Core Graphics中內置方法繪製圖像是存在這種問題的,如何解決呢?
其實圖形上下文只要沿着x軸旋轉180度,而後向上平移適當的高度便可(可是注意不要沿着z軸旋轉,這樣得不到想要的結果)。但是經過前面介紹的CGContextRotateCTM方法只能經過沿着z軸旋轉,此時不妨使用另一種方法,那就是在y軸方向縮放-1,一樣能夠達到想要的效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
-(void)drawImage2:(CGContextRef)context{ UIImage *image=[UIImage imageNamed:@"image2.jpg"]; CGSize size=[UIScreen mainScreen].bounds.size; CGContextSaveGState(context); CGFloat height=450,y=50; //上下文形變 CGContextScaleCTM(context, 1.0, -1.0);//在y軸縮放-1至關於沿着x張旋轉180 CGContextTranslateCTM(context, 0, -(size.height-(size.height-2*y-height)));//向上平移 //圖像繪製 CGRect rect= CGRectMake(10, y, 300, height); CGContextDrawImage(context, rect, image.CGImage);
CGContextRestoreGState(context); } |
在UIView的drawRect:中繪製的圖形會在控件顯示的時候調用(並且顯示時會重繪全部圖形),有時候咱們但願繪製內容的顯示是實時的,此時咱們就須要調用繪圖方法從新繪製,可是在iOS開發中不容許開發者直接調用drawRect:方法,刷新繪製內容須要調用setNeedsDisplay方法。下面以一個調整字體大小的界面演示一下視圖的刷新。
KCView.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// // KCView.h // RefreshView // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. //
#import <UIKit/UIKit.h>
@interface KCView : UIView
@property (nonatomic,copy) NSString *title;
@property (nonatomic,assign) CGFloat fontSize;
@end |
KCView.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// // KCView.m // RefreshView // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. //
#import "KCView.h"
@implementation KCView
-(void)drawRect:(CGRect)rect{
NSString *str=_title; UIFont *font=[UIFont fontWithName:@"Marker Felt" size:_fontSize]; UIColor *foreignColor=[UIColor redColor]; [str drawInRect:CGRectMake(100, 120, 300, 200) withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:foreignColor}]; }
@end |
KCMainViewController.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
// // KCMainViewController.m // RefreshView // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. //
#import "KCMainViewController.h" #import "KCView.h"
@interface KCMainViewController (){ KCView *_contentView; NSArray *_fontSize; }
@end
@implementation KCMainViewController
- (void)viewDidLoad { [super viewDidLoad];
[self initLayout];
[self addPickerView]; }
-(void)initLayout{ _fontSize=@[@15,@18,@20,@22,@25,@28,@30,@32,@35,@40]; _contentView=[[KCView alloc]initWithFrame:CGRectMake(0, 0, 320, 300)]; _contentView.backgroundColor=[UIColor whiteColor]; _contentView.title=@"Hello world!"; _contentView.fontSize=[_fontSize[0] intValue]; [self.view addSubview:_contentView]; }
-(void)addPickerView{ UIPickerView *picker=[[UIPickerView alloc]initWithFrame:CGRectMake(0, 300, 320, 268)]; picker.dataSource=self; picker.delegate=self;
[self.view addSubview:picker]; }
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{ return 1; } -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ return _fontSize.count; }
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{ return [NSString stringWithFormat:@"%@號字體",_fontSize[row] ]; } -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ _contentView.fontSize=[[_fontSize objectAtIndex:row] intValue];
//刷新視圖 [_contentView setNeedsDisplay]; } @end |
運行效果:
前面咱們也說過,Quartz 2D的圖形上下方除了能夠繪製到層上還能夠繪製到位圖、PDF等,這裏咱們就介紹一下如何利用Quartz 2D繪製圖像到位圖及PDF中。
上面的示例中一直都是在drawRect:方法中利用UIGraphicsGetCurrentContext()方法取得上下文,要獲得位圖或者PDF的上下文能夠利用UIGraphicsBeginImageContext(CGSize size)和UIGraphicsBeginPDFPageWithInfo(CGRect bounds, NSDictionary *pageInfo)方法。位圖圖形上下文和PDF圖形上下文UIKit是不會負責建立的,因此須要用戶手動建立,而且在使用完後關閉它。在使用UIKit中系統建立的圖形上下文的時候,咱們只能在drawRect:方法中使用,因爲這兩類圖形上下文是由咱們手動建立的所以能夠放到任何方法中調用。此外,這兩個方法開啓的圖形上下文並無返回值,若是咱們要獲得咱們建立的圖形上下文只要在建立上下文以後、關閉以前調用UIGraphicsGetCurrentContext()方法,此時取得的上下文便是咱們本身建立的圖形上下文。
下面利用位圖圖形上下文給一個圖片添加水印,在下面的程序中咱們首先建立上下文,而後在上下文中繪製圖片、直線和文本,最後從當前位圖上下文中取得最終造成的新圖片顯示到界面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
// // KCMainViewController.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <CoreText/CoreText.h> #import "KCMainViewController.h" #import "KCView.h" #import "KCView2.h" #import "KCView3.h"
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad { [super viewDidLoad];
UIImage *image=[self drawImageAtImageContext]; UIImageView *imageView=[[UIImageView alloc]initWithImage:image]; imageView.center=CGPointMake(160, 284);
[self.view addSubview:imageView]; }
#pragma mark 利用位圖上下文添加水印效果 -(UIImage *)drawImageAtImageContext{ //得到一個位圖圖形上下文 CGSize size=CGSizeMake(300, 188);//畫布大小 UIGraphicsBeginImageContext(size);
UIImage *image=[UIImage imageNamed:@"photo2.png"]; [image drawInRect:CGRectMake(0, 0, 300, 188)];//注意繪圖的位置是相對於畫布頂點而言,不是屏幕
//添加水印 CGContextRef context=UIGraphicsGetCurrentContext(); CGContextMoveToPoint(context, 200, 178); CGContextAddLineToPoint(context, 270, 178);
[[UIColor redColor]setStroke]; CGContextSetLineWidth(context, 2);
CGContextDrawPath(context, kCGPathStroke);
NSString *str=@"Kenshin Cui"; [str drawInRect:CGRectMake(200, 158, 100, 30) withAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Marker Felt" size:15],NSForegroundColorAttributeName:[UIColor redColor]}];
//返回繪製的新圖形 UIImage *newImage=UIGraphicsGetImageFromCurrentImageContext();
//最後必定不要忘記關閉對應的上下文 UIGraphicsEndImageContext();
//保存圖片 // NSData *data= UIImagePNGRepresentation(newImage); // [data writeToFile:@"/Users/kenshincui/Desktop/myPic.png" atomically:YES];
return newImage; }
@end |
運行效果:
注意:上面這種方式繪製的圖像除了能夠顯示在界面上還能夠調用對應方法進行保存(代碼註釋中已經包含保存方法);除此以外這種方法相比在drawRect:方法中繪製圖形效率更高,它不用每次展現時都調用全部圖形繪製方法。
繪製到PDF則要啓用pdf圖形上下文,PDF圖形上下文的建立使用方式跟位圖圖形上下文是相似的,須要注意的一點就是繪製內容到PDF時須要建立分頁,每頁內容的開始都要調用一次IGraphicsBeginPDFPage();方法。下面的示例演示了文本繪製和圖片繪製(其餘圖形繪製也是相似的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
// // KCMainViewController.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <CoreText/CoreText.h> #import "KCMainViewController.h" #import "KCView.h" #import "KCView2.h"
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- ( |