轉(zhuǎn)載自:http://m.2cto.com/kf/201612/572489.html
?
UIKIt UIBezierPath Core Graphics OpenGL ES Quartz2D的區(qū)別和聯(lián)系
UIKIt:UIKit中的控件都是基于Core Graphics實現(xiàn)的
UIBezierPath:UIBezierPath屬于UIKit,它是蘋果對復(fù)雜的Core Graphics進(jìn)行的封裝,方便我們用OC語言進(jìn)行簡單的繪圖
Core Graphics:是一套基于C語言的API,支持向量圖形,線、形狀、圖案、路徑、剃度、位圖圖像和pdf 內(nèi)容的繪制。
OpenGL ES:OpenGL是由SGI公司開發(fā)的一套3D圖形軟件接口標(biāo)準(zhǔn),它只是一個標(biāo)準(zhǔn),具體實現(xiàn)由機械制造商來完成,所以不同的機器他的效果可能是完全不一樣的。OpenGL-ES版本,主要是應(yīng)對嵌入式環(huán)境和應(yīng)用的要求,應(yīng)該說在高效完成2D/3D界面的同時,達(dá)到了降低功耗的效果。
Quartz2D:quartz是一個通用的術(shù)語,用于描述在IOS整個媒體層用到的多種技術(shù) 包括圖形、動畫、音頻、適配。Quart 2D 是一組二位繪圖和渲染API,Core Graphic會使用到這組API
Core Graphics框架
繪圖我們要用到Core Graphics框架,Core Graphics Framework是一套基于C的API框架,使用了Quartz作為繪圖引擎。該框架可以用于基于路徑的繪圖、變換、顏色管理、脫屏渲染,模板、漸變、遮蔽、圖像數(shù)據(jù)管理、圖像的創(chuàng)建、遮罩以及PDF文檔的創(chuàng)建、顯示和分析。
Core Graphics API所有的操作都在一個上下文中進(jìn)行。所以在繪圖之前需要獲取該上下文并傳入執(zhí)行渲染的函數(shù)中。介紹兩種最為常用的獲取方法。
第一種方法就是創(chuàng)建一個圖片類型的上下文。調(diào)用UIGraphicsBeginImageContextWithOptions函數(shù)就可獲得用來處理圖片的圖形上下文。利用該上下文,你就可以在其上進(jìn)行繪圖,并生成圖片。調(diào)用UIGraphicsGetImageFromCurrentImageContext函數(shù)可從當(dāng)前上下文中獲取一個UIImage對象。記住在你所有的繪圖操作后別忘了調(diào)用UIGraphicsEndImageContext函數(shù)關(guān)閉圖形上下文。
第二種方法是利用cocoa為你生成的圖形上下文。當(dāng)你子類化了一個UIView并實現(xiàn)了自己的drawRect:方法后,一旦drawRect:方法被調(diào)用,Cocoa就會為你創(chuàng)建一個圖形上下文,此時你對圖形上下文的所有繪圖操作都會顯示在UIView上。
什么是Quartz2D繪圖引擎
Quartz 2D是?個二維繪圖引擎,iOS中?部分控件的內(nèi)容都是通過Quartz 2D畫出來的。 Quartz 2D是純C語言的 Quartz 2D的API來自于CoreGraphics框架 數(shù)據(jù)類型和函數(shù)基本都是CG作為前綴
CGContextRef CGPathRef CGContextStrokePath(ctx)
Quartz 2D能完成的工作
繪制圖形 : 線條\三角形\矩形\圓\弧形 繪制文字 繪制\生成圖片(圖像) 讀取\生成PDF 截圖\裁剪圖片 自定義UI控件
Quartz2D繪圖有以下兩種方式

注意: UIBezierPath 對象可以獨立使用, 無需手動獲取“圖形上下文”對象,此處為了更好的理解“圖形上下文對象”所以暫時還是采用手動獲取“圖形上下文”對象的方式來繪圖。<?喎?"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="drawrect方法的使用">drawRect:方法的使用View內(nèi)部有個layer(圖層)屬性,drawRect:方法中取得的是一個Layer Graphics Context,因此,繪制的東西其實是繪制到view的layer上去了 iOS的繪圖操作是在UIView類的drawRect方法中完成的,重寫drawRect方法,在這里進(jìn)行繪圖操作,程序會自動調(diào)用此方法進(jìn)行繪圖。 重繪操作仍然在drawRect方法中完成,蘋果要求我們調(diào)用UIView類中的setNeedsDisplay方法,則程序會自動調(diào)用drawRect方法進(jìn)行重繪。(調(diào)用setNeedsDisplay會自動調(diào)用drawRect)。 在UIView中,重寫drawRect: (CGRect) aRect方法,可以自己定義想要畫的圖案.且此方法一般情況下只會畫一次.也就是說這個drawRect方法一般情況下只會被掉用一次. 當(dāng)某些情況下想要手動重畫這個View,只需要掉用[self setNeedsDisplay]方法即可. rect指的就是當(dāng)前view的bounds,即當(dāng)前視圖的所有范圍。 drawRect:方法是系統(tǒng)幫我們調(diào)用的,不能手動去調(diào)用這個方法。原因是手動去調(diào)用drawRect方法的時候無法保證系統(tǒng)已經(jīng)幫我們創(chuàng)建好了”圖形上下文”,所以這樣就無法保證在drawRect:方法中獲取”圖形上下文”對象,也就無法進(jìn)行繪圖了。 當(dāng)這個view第一次顯示的時候會調(diào)用一次drawRect方法;
當(dāng)這個view執(zhí)行重繪操作的時候會重新調(diào)用drawRect方法;
通過調(diào)用[self setNeedsDisplay] 【重繪整個view】或[self setNeedsDisplayInRect:]【重繪指定區(qū)域】的方式讓view執(zhí)行drawRect方法進(jìn)行重繪。
圖形上下文CGContextRef
圖形上下文的本質(zhì):是一個Quartz2D的繪圖環(huán)境!

幾種不同的渲染方式



c語言的api繪制基本圖形">通過C語言的API繪制基本圖形
#pragma mark - 通過C語言的API繪制基本圖形
- (void)drawRect:(CGRect)rect {
// 1.獲取上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.設(shè)置起點
CGContextMoveToPoint(cxtRef, 50, 50);
// 3.添加線
CGContextAddLineToPoint(cxtRef, 150, 150);
// 3.2 繼續(xù)加線段
CGContextAddLineToPoint(cxtRef, 250, 50);
// 3.3 向下的線
// 注意:如果想要新開線,需要重新設(shè)置起點!
CGContextMoveToPoint(cxtRef, 150, 150);
CGContextAddLineToPoint(cxtRef, 150, 250);
// 4.渲染
// kCGPathFill 將線段包含的部分渲染成黑色!
// kCGPathStroke 只負(fù)責(zé)繪制線條!
CGContextDrawPath(cxtRef, kCGPathStroke);
}UIBezierPath進(jìn)行直線圖形繪制
#pragma mark - 通過UIBezierPath進(jìn)行圖形繪制
// 不用寫關(guān)于圖形上下文的代碼,已經(jīng)封裝了,但是本質(zhì),還是需要獲取到view相關(guān)的圖形上下文,進(jìn)行繪制!
- (void)drawRect:(CGRect)rect {
// 1.獲取圖形上下文
// CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.創(chuàng)建路徑
UIBezierPath *path = [UIBezierPath bezierPath];
// 3.1 設(shè)置起點
[path moveToPoint:CGPointMake(50, 50)];
// 3.2 添加線
[path addLineToPoint:CGPointMake(150, 150)];
[path addLineToPoint:CGPointMake(250, 50)];
// 4.渲染--不寫圖形上下文的代碼時,直接使用此方法渲染繪制
[path stroke];
// 4.將路徑對象添加到圖形上下文
// CGContextAddPath(cxtRef, path.CGPath);
// 5.渲染
// CGContextDrawPath(cxtRef, kCGPathStroke);
}UIBezierPath繪制矩形
// MARK: - 1.矩形
- (void)drawRect:(CGRect)rect {
// 1.獲取圖形上下文
// CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.創(chuàng)建矩形的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 80)];
// 3.渲染
[path stroke];
// 3.添加路徑到圖形上下文
// CGContextAddPath(cxtRef, path.CGPath);
// 4.渲染
// CGContextDrawPath(cxtRef, kCGPathStroke);
}UIBezierPath繪制圓角矩形
// MARK: - 2.圓角矩形
- (void)drawRect:(CGRect)rect {
// // 1.獲取圖形上下文
// CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.創(chuàng)建路徑
CGFloat radius = 20;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 80) cornerRadius:radius];
// 3.渲染
[path stroke];
// // 3.添加路徑
// CGContextAddPath(cxtRef, path.CGPath);
// // 4.渲染
// CGContextDrawPath(cxtRef, kCGPathStroke);
}UIBezierPath繪制橢圓
// // 1.獲取圖形上下文
// CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.創(chuàng)建橢圓的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 200, 80)];
// 3.渲染
[path stroke];
// // 3.添加
// CGContextAddPath(cxtRef, path.CGPath);
// // 4.渲染
// CGContextDrawPath(cxtRef, kCGPathStroke);UIBezierPath繪制弧形
- (void)drawRect:(CGRect)rect {
// MARK: - 4.弧形
// // 1.獲取上下文
// CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.圓弧路徑
/**
center 圓心
radius 半徑
startAngle 起始角度
endAngle 結(jié)束角度
clockwise 是否為順時針!
*/
// 圓心
CGPoint center = CGPointMake(150, 150);
// 半徑
CGFloat radius = 100;
// 起始角度
CGFloat startA = 0;
// 結(jié)束角度
CGFloat endA = M_PI_2;
// 是否為順時針
BOOL clockwise = NO;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];
// 3.渲染
[path stroke];
// // 3.添加路徑
// CGContextAddPath(cxtRef, path.CGPath);
// // 4.渲染
// CGContextDrawPath(cxtRef, kCGPathStroke);
}UIBezierPath繪制圓形
// 2.圓路徑
/**
center 圓心
radius 半徑
startAngle 起始角度
endAngle 結(jié)束角度
clockwise 是否為順時針!
*/
// 圓心
CGPoint center = CGPointMake(150, 150);
// 半徑
CGFloat radius = 100;
// 起始角度
CGFloat startA = 0;
// 結(jié)束角度
CGFloat endA = M_PI * 2;
// 是否為順時針
BOOL clockwise = NO;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];
// 渲染
[path stroke];UIBezierPath繪制扇形
// 2.圓弧路徑
/**
center 圓心
radius 半徑
startAngle 起始角度
endAngle 結(jié)束角度
clockwise 是否為順時針!
*/
// 圓心
CGPoint center = CGPointMake(150, 150);
// 半徑
CGFloat radius = 100;
// 起始角度
CGFloat startA = 0;
// 結(jié)束角度
CGFloat endA = M_PI_4;
// 是否為順時針
BOOL clockwise = YES;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];
// 2.2 向圓心添加線
[path addLineToPoint:center];
// 渲染
[path fill];繪圖狀態(tài)信息設(shè)置
1> 設(shè)置線寬 CGContextSetLineWidth
2> 設(shè)置線頭樣式 CGContextSetLineCap
3> 設(shè)置線段接頭樣式 CGContextSetLineJoin
4> 顏色的設(shè)置 [[UIColor redColor] set]
- (void)drawRect:(CGRect)rect {
// // 1.獲取圖形上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.創(chuàng)建矩形的路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50, 50)];
// 第一根線
[path addLineToPoint:CGPointMake(150, 150)];
// 第二根線
[path addLineToPoint:CGPointMake(250, 50)];
// 3.添加路徑到圖形上下文
CGContextAddPath(cxtRef, path.CGPath);
// 3.1 設(shè)置線寬!
CGContextSetLineWidth(cxtRef, 20);
// 3.2 設(shè)置線頭樣式
/**
kCGLineCapButt, 比較難看的!
kCGLineCapRound, 圓角
kCGLineCapSquare 平角
*/
CGContextSetLineCap(cxtRef, kCGLineCapButt);
// 3.3 設(shè)置接頭的樣式
/**
kCGLineJoinMiter, 尖頭
kCGLineJoinRound, 圓角
kCGLineJoinBevel 多余的部分感覺被切了!
*/
CGContextSetLineJoin(cxtRef, kCGLineJoinMiter);
// 3.4 顏色的設(shè)置
[[UIColor redColor] setStroke];
[[UIColor yellowColor] setFill];
// 4.渲染
CGContextDrawPath(cxtRef, kCGPathFillStroke);
}
兩種填充原則
- (void)drawRect:(CGRect)rect {
#pragma mark - 非零繞數(shù)填充規(guī)則
[self nonzeroWindingNumberRule];
#pragma mark - 奇偶填充規(guī)則
[self evenOddRule];
}
#pragma mark - 非零繞數(shù)填充規(guī)則
/**
* 非零繞數(shù)填充規(guī)則:
* 在圖形上下文中的任何一個點
* 被順時針覆蓋標(biāo)記為 1
* 被逆時針覆蓋標(biāo)記為 -1
* 當(dāng)標(biāo)記為0的時候不填充,其他則填充
* 這個規(guī)則與方向有關(guān),與次數(shù)無關(guān)
*/
- (void)nonzeroWindingNumberRule {
// 1.獲取圖形上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.繪制順時針的圓形
UIBezierPath *arcY = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
// 3.繪制逆時針的圓形
UIBezierPath *arcN = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:75 startAngle:0 endAngle:M_PI * 2 clockwise:NO];
// 驗證與次數(shù)無關(guān)
UIBezierPath *arc3 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 230) radius:50 startAngle:0 endAngle:M_PI * 2 clockwise:NO];
// 4.添加路徑對象
CGContextAddPath(cxtRef, arcY.CGPath);
CGContextAddPath(cxtRef, arcN.CGPath);
CGContextAddPath(cxtRef, arc3.CGPath);
[[UIColor yellowColor] setFill];
// 5.渲染
CGContextDrawPath(cxtRef, kCGPathFill);
}
#pragma mark - 奇偶填充規(guī)則
// even:偶數(shù) odd:偶數(shù)
/**
* 奇偶填充規(guī)則解釋:
* 在圖形上下文中的任何一個點
* 如果它被路徑對象覆蓋了奇數(shù)次,在進(jìn)行渲染操作的時候這個點就會被渲染
* 如果它被路徑對象覆蓋了偶數(shù)次,在進(jìn)行渲染操作的時候這個點就不會被渲染
*/
- (void)evenOddRule {
// 獲取圖形上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 繪制水平方向矩形
UIBezierPath *rectH = [UIBezierPath bezierPathWithRect:CGRectMake(50, 100, 200, 100)];
// 繪制圓形
UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:75 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
// 繪制豎直方向的矩形
UIBezierPath *rectV = [UIBezierPath bezierPathWithRect:CGRectMake(180, 25, 50, 250)];
// 將路徑對象添加到圖形上下文中
CGContextAddPath(cxtRef, rectH.CGPath);
CGContextAddPath(cxtRef, arcPath.CGPath);
CGContextAddPath(cxtRef, rectV.CGPath);
[[UIColor orangeColor] setFill];
// 同時顯示stroke和fill的顏色
CGContextDrawPath(cxtRef, kCGPathEOFill);
}

模擬下載進(jìn)度條
思路:
在控制器中將 slider 的值傳遞給自定義 view
在自定義 View中,根據(jù)傳遞過來的值繪制弧。
創(chuàng)建一個與自定義 view 一樣大小的 label 來顯示下載進(jìn)度
#import @interface DownloadView : UIView /** * 接收滑塊的值 */ @property (nonatomic, assign) float sliderValue; @end
#import "DownloadView.h"
@interface DownloadView ()
// 需要先寫,再拖線!
/**
* 顯示百分比的label
*/
@property (nonatomic, weak) IBOutlet UILabel *valueLbl;
@end
@implementation DownloadView
#pragma mark - 重寫set方法,執(zhí)行重繪操作
- (void)setSliderValue:(float)sliderValue {
_sliderValue = sliderValue;
// 內(nèi)部執(zhí)行重繪操作
[self setNeedsDisplay];
self.valueLbl.text = [NSString stringWithFormat:@"%.2f%%", (sliderValue * 100)];
}
#pragma mark - 繪制下載進(jìn)度條
- (void)drawRect:(CGRect)rect {
// 1.獲取上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.創(chuàng)建路徑
CGFloat startA = -M_PI_2;
CGFloat endA = self.sliderValue * M_PI * 2 - M_PI_2;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:150 startAngle:startA endAngle:endA clockwise:YES];
// 保證繪制出來是扇形
[path addLineToPoint:CGPointMake(150, 150)];
// 3.添加路徑
CGContextAddPath(cxtRef, path.CGPath);
// 4.渲染
CGContextDrawPath(cxtRef, kCGPathFill);
}
@end#import "ViewController.h"
#import "DownloadView.h"
@interface ViewController ()
/**
* 下載進(jìn)度的視圖
*/
@property (nonatomic, weak) IBOutlet DownloadView *downloadView;
@end
@implementation ViewController
#pragma mark - 滑動事件
- (IBAction)sliderValueChanged:(UISlider *)sender {
NSLog(@"現(xiàn)在的值是 %f", sender.value);
// 傳遞值給自定義view
self.downloadView.sliderValue = sender.value;
}
@end
餅狀圖
思路:
構(gòu)建數(shù)據(jù),NSArray *data = @[@30, @15, @5, @17, @3, @10, @20];。
根據(jù)數(shù)據(jù)個數(shù)繪制“扇形”(?。?br>注意:
每個弧的起始、結(jié)束弧度都是不一樣的
每次繪制完畢一個弧以后都要重新設(shè)置下一次的起始弧度為當(dāng)前的結(jié)束弧度
本次繪制的結(jié)束弧度,為起始弧度+本次的弧度
- (void)drawRect:(CGRect)rect {
// 1.數(shù)據(jù)
NSArray *data = @[@30, @15, @5, @17, @3, @10, @20];
// 1.2 獲取圖形上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.遍歷
// 2.0.1 圓心,半徑
CGPoint center = CGPointMake(150, 150);
CGFloat radius = 120;
BOOL clockwise = YES;
// 2.0.2 定義起始角度的變量
CGFloat startA = 0;
for (NSInteger i = 0; i < data.count; i++) {
// 2.1 取出數(shù)據(jù),并轉(zhuǎn)為int類型
float number = [data[i] floatValue];
// 2.2 計算所占用的角度
CGFloat AngleOfNum = number / 100 * (M_PI * 2);
// 2.3 計算結(jié)束角度
CGFloat endA = startA + AngleOfNum;
// 2.4 創(chuàng)建路徑對象
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];
// 2.4.2 添加線
[path addLineToPoint:center];
// 2.5 添加路徑
CGContextAddPath(cxtRef, path.CGPath);
// 隨機色
[[UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0] setFill];
// 2.6 渲染
CGContextDrawPath(cxtRef, kCGPathFill);
// 2.7 改變角度
startA = endA;
}
}