在iOS上畫圖主要有3種方法:
- UIKit, 這是我們最常用的繪圖方法,平時的UIButton、UIImageView就是使用UIkit實現(xiàn)的。
- Core Graphics, 這是是蘋果提供一套基于C的API框架,使用了Quartz2D作為繪圖引擎。Quartz2D是既可以在iOS上使用,也可以在Mac OS上使用的垮平臺2D繪圖引擎。
- OpenGL ES, 是種編程規(guī)范,也就是說它只定義了一套規(guī)范,具體的實現(xiàn)由設(shè)備制造商根據(jù)規(guī)范去做。
在上節(jié)UIBezierPath畫一個拱形的tabBar圖形章節(jié)中,我們使用它畫了一個帶拱形的View??梢钥吹経IBezierPath其實就是對Core Graphics的封裝,直接調(diào)用API就可以簡單的畫直線、圓、曲線,省去了對Path的操作細節(jié)。
一. Core Graphics使用簡介
Core Graphics是基于C語言的API,所以它不是面向?qū)ο蟮模谑褂脮r需要一個圖形上下(CGContext)。Context其實就是暫時緩存圖形,等繪制完畢后將圖形返還給所需的對象。
1.1 如何在UIView上使用Graphics繪圖?
使用Core Graphics需要一個畫布(CGContext),而UIView只能在-(void)drawRect:(CGRext)rect方法中獲取當前UIView的相關(guān)聯(lián)的圖形上下文,所以Core Graphics只能在-(void)drawRect:(CGRext)rect方法中使用。
1.2 Core Graphics繪圖七大步驟
- 獲取上下文
- 設(shè)置畫圖起點
- 設(shè)置繪圖類別:直線、圓、圓弧、矩形
- 設(shè)置繪圖線條顏色和線條寬度
- 開始繪圖
- 結(jié)束繪圖
1.3 繪制實例
- 繪制直線:
-(void)drawRect:(CGRect)rect
{
// 1. 獲取圖像上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2. 設(shè)置起點
CGContextMoveToPoint(ctx, 10, 100);
// 3. 畫直線
CGContextAddLineToPoint(ctx, 100, 100);
// 4. 設(shè)置線條顏色 紅色
CGContextSetRGBStrokeColor(ctx, 1.0, 0, 0, 1.0);
// 5. 設(shè)置線條寬度
CGContextSetLineWidth(ctx, 10);
// 6. 設(shè)置線條的起點和終點的樣式
CGContextSetLineCap(ctx, kCGLineCapRound);
// 7. 設(shè)置線條的轉(zhuǎn)角的樣式
CGContextSetLineJoin(ctx, kCGLineJoinRound);
// 8. 開始繪制
CGContextStrokePath(ctx);
}

- 繪制矩形
-(void)drawRect:(CGRect)rect
{
// 繪制四邊形
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.繪制四邊形
CGContextAddRect(ctx, CGRectMake(10, 10, 150, 100));
[[UIColor colorWithRed:1.0 green:0 blue:0 alpha:1.0] set];
// 3.渲染圖形到layer上
// 填充顏色
CGContextFillPath(ctx);
// 路徑顏色
// CGContextStrokePath(ctx);
}

- 繪制圓形
-(void)drawRect:(CGRect)rect
{
// 畫圓弧
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.畫圓弧
// x/y 圓心
// radius 半徑
// startAngle 開始的弧度
// endAngle 結(jié)束的弧度
// clockwise 畫圓弧的方向 (0 順時針, 1 逆時針)
CGContextAddArc(ctx, 100, 100, 50, 0, M_PI * 2, 1);
// CGContextClosePath(ctx);
// 3.設(shè)置線條顏色
[[UIColor redColor] set];
// 4. 繪圖
CGContextStrokePath(ctx);
// CGContextFillPath(ctx);
}

思考:
- CGContextStrokePath(ctx); 和 CGContextFillPath(ctx);有啥區(qū)別?
- 如何設(shè)置圓形的線條顏色以及實心圓的背景色?
1.4 如何在drawRect方法外繪圖
使用UIGraphicsGetCurrentContext()繪圖有個限制條件就是必須在-(void)drawRect:(CGRect)rect方法中使用,實際應(yīng)用中顯然不夠靈活。那么如何在此方法以外的地方繪制圖形了?常用的有兩種方法:
a. UIGraphicsBeginImageContext(size)繪制位圖
b. 貝塞爾曲線+CAShapeLayer繪圖
- 使用UIGraphicsBeginImageContext(CGSize size)繪圖
-(void)viewDidLoad
{
[super viewDidLoad];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
imgView.image = [UIImage imageNamed:@"liuYan.jpeg"];
imgView.backgroundColor = [UIColor blueColor];
UIImage *image = [UIImage imageNamed:@"liuYan.jpeg"];
NSLog(@"圖片大小%f %f", image.size.width,image.size.height );
// 我們重新繪圖,將圖片在100x100大小的畫布上繪制
CGSize size = CGSizeMake(100, 100);
// 設(shè)置畫圖大小
UIGraphicsBeginImageContext(CGSizeMake(size.width, size.height));
[image drawInRect:CGRectMake(0,0,100, 100)];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
NSLog(@"修改后大小%f %f", scaledImage.size.width,scaledImage.size.height );
imgView.image = scaledImage;
UIGraphicsEndImageContext();
}

從代碼可以看到我們使用UIGraphicsBeginImageContext(CGSize size)這個方法也可以繪制位圖,而不需要在drawRect方法中繪制。
打印圖片繪制前后大?。?/p>

從截圖中可以看到我們將500x600大小的圖片重新繪制后,產(chǎn)生的新圖片scaledImage大小為100x100了。
UIGraphicsBeginImageContext(CGSize size)方法中的CGSize size參數(shù)即圖形上下文中的畫布大小,通過改變這個參數(shù)值,繪制出隨意大小的圖片。
-(void)drawInRect:(CGRect)rect; 方法中的rect參數(shù)告訴畫布我需要在哪個點上畫多大的圖像。例如將 [image drawInRect:CGRectMake(0,0,100, 100)];改為 [image drawInRect:CGRectMake(25,25,50, 50)];

圖像跑到了畫布的中間位置,并且圖像大小變成了50x50,但scaledImage仍然是100x100,說明畫布的其他位置并沒有東西填充。
P.S 使用此方法可以將一張圖片進行縮放。
- 貝塞爾曲線+CAShapeLayer繪圖
-(void)viewDidLoad {
[super viewDidLoad];
UIView *rectView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
rectView.backgroundColor = [UIColor redColor];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rectView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(20,5)];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.frame = rectView.bounds;
layer.path = path.CGPath;
rectView.layer.mask = layer;
[self.view addSubview:rectView];
}

Core Graphic部分代碼上傳gitHub。
1.5 Core Graphics畫文字和圖片
主要是 darwAtPoint和drawInRect 這兩個方法的使用
// 畫文字
-(void)drawRect:(CGRect)rect {
// Drawing code
NSString *str = @"Quartz 2D是一個二維繪圖引擎,同時支持iOS和Mac系統(tǒng)";
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
//設(shè)置文字大小
dict[NSFontAttributeName] = [UIFont systemFontOfSize:12];
//設(shè)置文字顏色
dict[NSForegroundColorAttributeName] = [UIColor greenColor];
//設(shè)置描邊寬度
dict[NSStrokeWidthAttributeName] = @2;
//設(shè)置描邊顏色
dict[NSStrokeColorAttributeName] = [UIColor blueColor];
//設(shè)置陰影
NSShadow *shadow = [[NSShadow alloc] init];
//設(shè)置陰影的便宜量
shadow.shadowOffset = CGSizeMake(1, 1);
//設(shè)置陰影顏色
shadow.shadowColor = [UIColor greenColor];
//設(shè)置陰影模糊程序
shadow.shadowBlurRadius = 1;
dict[NSShadowAttributeName] = shadow;
/**
AtPoint:文字所畫的位置
withAttributes:描述文字的屬性.
*/
//不會自動換行
[str drawAtPoint:CGPointMake(0,0) withAttributes:dict];
//會自動換行.
[str drawInRect:self.bounds withAttributes:dict];
}
// 畫圖片
-(void)drawRect:(CGRect)rect {
// Drawing code
//1.加載圖片
UIImage *image = [UIImage imageNamed:@"liuYan.jpeg"];
//繪制出來的圖片,是保持原來圖片大小
[image drawAtPoint:CGPointZero];
//把圖片填充到這個rect當中.
[image drawInRect:rect];
//添加裁剪區(qū)域 .把超區(qū)裁剪區(qū)域以外都裁剪掉
// UIRectClip(CGRectMake(0, 0, 50, 50));
//平鋪
// [image drawAsPatternInRect:self.bounds];
// //快速的畫出一個矩形
// [[UIColor blueColor] set];
// UIRectFill(CGRectMake(10, 10, 100, 100));
}
二. UIBezierPath使用簡介
UIBezierPath其實是對Core Graphics Path的封裝, 需要與CAShapeLayer配合使用。
2.1 UIBezierPath使用六部曲
- 創(chuàng)建貝塞爾曲線
+(instancetype)bezierPath;
- 添加子路徑
// 從point開始繪制路徑
-(void)moveToPoint:(CGPoint)point;
- 設(shè)置繪圖類別:直線、圓弧、矩形
// 添加一條直線路徑
-(void)addLineToPoint:(CGPoint)point;
// 添加一個圓形路徑
-(void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle
// 添加一段圓弧路徑
-(void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
- 設(shè)置路徑顏色
[[UIColor redColor] set];
- 開始畫路徑
[path stroke];
- 結(jié)束路徑繪制
-(void)closePath;
主要就以上6個步驟就可以快速的畫出圖形。
注意:如果需要畫一個實心View,可以使用 [[UIColor blackColor] setFill];設(shè)置填充顏色。
2.2 繪制實例
- 直線
-(void)drawRect:(CGRect)rect
{
//1.獲取圖形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//上下文的狀態(tài)(線的粗細、顏色、鏈接樣式等)
//設(shè)置線的粗細
CGContextSetLineWidth(ctx, 10);
CGContextSetLineJoin(ctx, kCGLineJoinRound);
CGContextSetLineCap(ctx, kCGLineCapRound);
//2. 繪制路徑
UIBezierPath *path = [UIBezierPath bezierPath];
//2.1 設(shè)置起點
[path moveToPoint:CGPointMake(50, 250)];
//2.2 添加一根線到終點
[path addLineToPoint:CGPointMake(250, 50)];
//畫第二條直線(新起點)
[path moveToPoint:CGPointMake(100, 250)];
[path addLineToPoint:CGPointMake(250, 100)];
//把上一條線的終點當做起點來畫第二條線
[path addLineToPoint:CGPointMake(250, 250)];
[[UIColor redColor] setStroke];
//3.把繪制的內(nèi)容添加到上下文中
// UIBezierPath:UIKit框架 ---> CGPathRef:CoreGraphics框架
CGContextAddPath(ctx, path.CGPath);
//4.把上下文的內(nèi)容顯示到view上(渲染到view的layer上)渲染的方式有兩種Stroke(描邊)、Fill(填充)
CGContextStrokePath(ctx);
}

- 矩形
-(void)drawRect:(CGRect)rect
{
//1.獲取圖形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//設(shè)置顏色
[[UIColor redColor]set];
//2.繪制路徑
//畫矩形
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 100, 100)];
//畫圓角矩形
// UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 100, 100) cornerRadius:50];
//3.把路徑添加到上下文
CGContextAddPath(ctx, path.CGPath);
//4.把上下文渲染到view上
//描邊
// CGContextStrokePath(ctx);
//填充
CGContextFillPath(ctx);
}

貝塞爾曲線部分代碼上傳到gitHub。
Quartz2D內(nèi)存管理
Quartz2D是C語言的框架,部分地方需要自己管理內(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ù)