首先我們通過圖-1了解iOS圖像編程的框架結(jié)構(gòu):

UIBezierPath位于UIKit,絕大多數(shù)圖形界面都由UIKit完成。從圖中可以看出UIKit依賴于Core Graphics框架,也是基于Core Graphics框架實現(xiàn)的。如果想要完成某些更底層的功能或者追求極致的性能,那么就要用到Core Graphics去完成。UIBezierPath是對Core Graphics框架中的CGPath的封裝。兩者的本質(zhì)上都是一樣的,都是使用Quartz來繪畫。只不過把繪圖操作暴露在不同的API層面上,在具體實現(xiàn)上,當然也會有一些細小的差別。
我們用Core Graphics來繪圖的一個通常原因就是只是用圖片或是圖層效果不能輕易地繪制出矢量圖形。矢量繪圖包含一下這些:
- 任意多邊形(不僅僅是一個矩形)
- 斜線或曲線
- 文本
- 漸變
在我們繪制圖形之前,我們需要獲取到一個圖形上下文CGContext,可以把他形象的比喻成畫畫的畫板,試想如果我們沒有畫板是不是就不能繪畫,我們進行繪圖也是如此,UIKit提供一個C函數(shù)來幫組我們獲取圖形上下文:UIGraphicsGetCurrentContext()?,F(xiàn)在我們對UIBezierPath、Core Graphics以及CGPath之間的關(guān)系有了一個大致的了解,那么接下來我通過一個例子來展示它們是如何繪制圖形的。

注意:如果不在drawRect:方法中調(diào)用UIGraphicsGetCurrentContext()函數(shù),其返回值為nil。當然我們也有其他方法來創(chuàng)建一個圖形上下文(譬如使用UIGraphicsBeginImageContext()),我會在代碼中為大家展示。
【按照文檔中的說法,系統(tǒng)會維護一個CGContextRef的棧,而UIGraphicsGetCurrentContext()會取棧頂?shù)腃GContextRef,正確的做法是只在drawRect里調(diào)用UIGraphicsGetCurrentContext(),因為在drawRect之前,系統(tǒng)會往棧里面壓入一個valid的CGContextRef,除非自己去維護一個CGContextRef,否則不應(yīng)該在其他地方取CGContextRef。】
code
自定義一個MyView使其繼承與UIView,重寫drawRect:方法:
//.h文件
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
//.m文件
#import "MyView.h"
@implementation MyView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor grayColor];
}
return self;
}
//使用UIBezierPath繪圖
- (void)drawWithBezierPath {
UIBezierPath *bPath = [UIBezierPath bezierPath];
[bPath moveToPoint:CGPointMake(0, 0)];
[bPath addCurveToPoint:CGPointMake(200, 200) controlPoint1:CGPointMake(50, 150) controlPoint2:CGPointMake(150, 50)];
[[UIColor greenColor] setStroke];
[bPath stroke];
}
//使用Graphics繪圖
- (void)drawWithGraphics {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(context, 200, 0);
CGContextAddCurveToPoint(context, 150, 150, 50, 50, 0, 200);
[[UIColor redColor] setStroke];
CGContextStrokePath(context);
}
//使用CGPath繪圖
- (void)drawWithCGPath {
CGContextRef gc = UIGraphicsGetCurrentContext();
//創(chuàng)建CGMutablePathRef
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, 100, 100, 45, 0, M_PI*2, YES);
//將CGMutablePathRef添加到當前Context內(nèi)
CGContextAddPath(gc, path);
//設(shè)置繪圖屬性
[[UIColor redColor] setStroke];
CGContextSetLineWidth(gc, 5);
//執(zhí)行繪畫
CGContextStrokePath(gc);
CGPathRelease(path);
}
//CGPath和UIBezierPath混合使用繪圖
- (void)drawWithBezierPathAndGraphics {
UIBezierPath* aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:100 startAngle:0 endAngle:M_PI*2 clockwise:YES];
CGPathRef cgPath = aPath.CGPath;
CGMutablePathRef mutablePath = CGPathCreateMutableCopy(cgPath);
CGPathAddRect(mutablePath, NULL, CGRectMake(50, 50, 100, 100));
aPath.CGPath = mutablePath;
aPath.lineWidth = 5;
[[UIColor greenColor] setStroke];
[aPath stroke];
CGPathRelease(mutablePath);
}
- (void)drawRect:(CGRect)rect {
// Drawing code
[self drawWithBezierPath];
[self drawWithGraphics];
[self drawWithCGPath];
[self drawWithBezierPathAndGraphics];
}
以上代碼只是簡單的演示如何用UIBezierPath和Core Graphics進行繪圖,大家可以細心觀察彼此之間的差別和聯(lián)系,也可以大膽的嘗試使用其他的繪圖API也繪制自己想繪制的圖形,譬如復雜的圖表、人物肖像、餅狀圖等等只要是基于矢量圖的都能實現(xiàn)。通過以上代碼我們可以發(fā)現(xiàn),如果我們需要繪制一張圖形,就必須自定義一個view并重寫其DrawRect:方法,哪怕只是一個簡單的矩形。這樣是不是未免有點麻煩,難道就沒有其他方法嗎?of course,當然有了,我就不賣關(guān)子了,直接上代碼:
//開始圖像繪圖
UIGraphicsBeginImageContext(self.view.bounds.size);
//獲取當前CGContextRef
CGContextRef gc = UIGraphicsGetCurrentContext();
//創(chuàng)建用于轉(zhuǎn)移坐標的Transform,這樣我們不用按照實際顯示做坐標計算
CGAffineTransform transform = CGAffineTransformMakeTranslation(50, 50);
//創(chuàng)建CGMutablePathRef
CGMutablePathRef path = CGPathCreateMutable();
//左眼
CGPathAddEllipseInRect(path, &transform, CGRectMake(0, 0, 20, 20));
//右眼
CGPathAddEllipseInRect(path, &transform, CGRectMake(80, 0, 20, 20));
//笑
CGPathMoveToPoint(path, &transform, 100, 50);
CGPathAddArc(path, &transform, 50, 50, 50, 0, M_PI, NO);
//將CGMutablePathRef添加到當前Context內(nèi)
CGContextAddPath(gc, path);
//設(shè)置繪圖屬性
[[UIColor blueColor] setStroke];
CGContextSetLineWidth(gc, 2);
//執(zhí)行繪畫
CGContextStrokePath(gc);
//從Context中獲取圖像,并顯示在界面上
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imgView = [[UIImageView alloc] initWithImage:img];
[self.view addSubview:imgView];
最后提醒一下大家,使用CoreFoundation框架創(chuàng)建對象,例如代碼中出現(xiàn)的:CGMutablePathRef path = CGPathCreateMutable();不要忘了在繪制完之后使用相應(yīng)的方法去釋放它們:CGPathRelease(path);。否則會造成內(nèi)存泄漏,ARC是不會幫組我們?nèi)ス芾鞢oreFoundation創(chuàng)建出來的對象。