第一步:先科普一下基礎(chǔ)知識:
Core Graphics是基于C的API,可以用于一切繪圖操作
Core Graphics 和Quartz 2D的區(qū)別
quartz是一個(gè)通用的術(shù)語,用于描述在IOS和MAC OS X ZHONG 整個(gè)媒體層用到的多種技術(shù) 包括圖形、動畫、音頻、適配。
Quart 2D 是一組二位繪圖和渲染API,Core Graphic會使用到這組API
Quartz Core 專指Core Animation用到的動畫相關(guān)的庫、API和類
點(diǎn)和像素的對比
系統(tǒng)擁有坐標(biāo)系,如320*480 硬件有retain屏幕和非retain屏:如320*480、640*960
Core Graphics 使用的是系統(tǒng)的坐標(biāo)系來繪制圖片。在分辨率為640*960手機(jī)上繪制圖片時(shí),實(shí)際上Core Graphics 的坐標(biāo)是320*480。這個(gè)時(shí)候每個(gè)坐標(biāo)系上的點(diǎn),實(shí)際上擁有兩個(gè)像素。
圖形上下文
Core Graphics 使用圖形上下文進(jìn)行工作,這個(gè)上下文的作用像畫家的畫布一樣。
在圖形上下文之外是無法繪圖的,我們可以自己創(chuàng)建一個(gè)上下文,但是性能和內(nèi)存的使用上,效率是非常低得。
我們可以通過派生一個(gè)UIView的子類,獲得它的上下文。在UIView中調(diào)用drawRect:方法時(shí),會自動準(zhǔn)備好一個(gè)圖形上下文,可以通過調(diào)用
UIGraphicsGetCurrentContext()來獲取。 因?yàn)樗沁\(yùn)行期間繪制圖片,我們可以動態(tài)的做一些額外的操作
Core Graphics的優(yōu)點(diǎn)
快速、高效,減小應(yīng)用的文件大小。同時(shí)可以自由地使用動態(tài)的、高質(zhì)量的圖形圖像。 使用Core Graphics,可以創(chuàng)建直線、路徑、漸變、文字與圖像等內(nèi)容,并可以做變形處理。
繪制自定義視圖
drawRect:是系統(tǒng)的方法,不要從代碼里面直接調(diào)用 drawRect:,而應(yīng)該使用setNeedsDisplay重繪.
需要知道的術(shù)語
路徑 path
陰影 shadow
筆畫 stroke
剪裁路徑 Clip Path
線條粗細(xì) Line Width
混合模式 Blend Mode
填充色 Fill Color
當(dāng)前形變矩陣 Current Transform Matrix
線條圖案 Line Dash
圖形上下文棧
一個(gè)圖形上下文好比是畫布上的一副扁平的圖畫 執(zhí)行繪畫動作,這些動作是在同一個(gè)圖層上完成的。 圖形上下文不允許將內(nèi)容分不到多個(gè)圖層中,如果有需求在不同圖層上畫,可以考慮使用視圖層次結(jié)構(gòu),創(chuàng)建多個(gè)UIView,并將他們作為父視圖的子視圖
圖形上下文棧可以把圖形上下文的當(dāng)前狀態(tài)保存下來,并在執(zhí)行一些動作后再次恢復(fù)回來
CGContextSaveGState();
CGContextStoreGState();
路徑、漸變、文字和圖像
1. 使用UIBezierPath創(chuàng)建路徑
2. 手動創(chuàng)建路徑 moveToPoint addLineToPoint addArcWithCenter addCurveToPoint
漸變,漸變可以在指定方向上,以可變的比率在一系列顏色之間轉(zhuǎn)化
線性漸變:沿著一條定義好了起點(diǎn)和重點(diǎn)的直線方向,呈線性變化。如果這條線有一定角度,線性漸變也會沿相同路徑變化
放射漸變:顏色順著兩個(gè)原型之間的方向線性變化,這兩個(gè)園為起始圓和終止圓,每隔圓都有自己的圓心和班級
文字
darwAtPoint
drawInRect
圖像
Core Graphics 不會保持圖像的長寬比例,Core Graphics會將圖像的邊界設(shè)置為CGrect,不管圖片是否變形 darwAtPoint drawInRect
第二步:代碼部分:
基礎(chǔ)畫法就不多講啦!都通用:
第一種繪圖形式:在UIView的子類方法drawRect:中繪制一個(gè)藍(lán)色圓,使用UIKit在Cocoa為我們提供的當(dāng)前上下文中完成繪圖任務(wù)。
?- (void) drawRect: (CGRect) rect {
UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
}
第二種繪圖形式:使用Core Graphics實(shí)現(xiàn)繪制藍(lán)色圓。
?- (void) drawRect: (CGRect) rect {
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
}
第三種繪圖形式:我將在UIView子類的drawLayer:inContext:方法中實(shí)現(xiàn)繪圖任務(wù)。drawLayer:inContext:方法是一個(gè)繪制圖層內(nèi)容的代理方法。為了能夠調(diào)用drawLayer:inContext:方法,我們需要設(shè)定圖層的代理對象。但要注意,不應(yīng)該將UIView對象設(shè)置為顯示層的委托對象,這是因?yàn)閁IView對象已經(jīng)是隱式層的代理對象,再將它設(shè)置為另一個(gè)層的委托對象就會出問題。輕量級的做法是:編寫負(fù)責(zé)繪圖形的代理類。在MyView.h文件中聲明如下代碼:
?@interfaceMyLayerDelegate : NSObject
@end
然后MyView.m文件中實(shí)現(xiàn)接口代碼:
?@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
直接將代理類的實(shí)現(xiàn)代碼放在MyView.m文件的#import代碼的下面,這樣感覺好像在使用私有類完成繪圖任務(wù)(雖然這不是私有類)。需要注意的是,我們所引用的上下文并不是當(dāng)前上下文,所以為了能夠使用UIKit,我們需要將引用的上下文轉(zhuǎn)變成當(dāng)前上下文。
因?yàn)閳D層的代理是assign內(nèi)存管理策略,那么這里就不能以局部變量的形式創(chuàng)建MyLayerDelegate實(shí)例對象賦值給圖層代理。這里選擇在MyView.m中增加一個(gè)實(shí)例變量,因?yàn)閷?shí)例變量默認(rèn)是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];// 調(diào)用此方法,drawLayer: inContext:方法才會被調(diào)用。
第四種繪圖形式:使用Core Graphics在drawLayer:inContext:方法中實(shí)現(xiàn)同樣操作,代碼如下:
?- (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con {
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
}
最后,演示UIGraphicsBeginImageContextWithOptions的用法,并從上下文中生成一個(gè)UIImage對象。生成UIImage對象的代碼并不需要等待某些方法被調(diào)用后或在UIView的子類中才能去做。
第五種繪圖形式:使用UIKit實(shí)現(xiàn):
?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函數(shù)參數(shù)的含義:第一個(gè)參數(shù)表示所要?jiǎng)?chuàng)建的圖片的尺寸;第二個(gè)參數(shù)用來指定所生成圖片的背景是否為不透明,如上我們使用YES而不是NO,則我們得到的圖片背景將會是黑色,顯然這不是我想要的;第三個(gè)參數(shù)指定生成圖片的縮放因子,這個(gè)縮放因子與UIImage的scale屬性所指的含義是一致的。傳入0則表示讓圖片的縮放因子根據(jù)屏幕的分辨率而變化,所以我們得到的圖片不管是在單分辨率還是視網(wǎng)膜屏上看起來都會很好。
第六種繪圖形式:使用Core Graphics實(shí)現(xiàn):
?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();
第三步:實(shí)踐部分:
第一種:基本圖形繪制
?/**
*? 什么調(diào)用:當(dāng)你視圖第一次顯示的時(shí)候就會調(diào)用
*? 作用:繪圖
*? @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 引用
// 目前學(xué)的上下文都跟UIGraphics有關(guān),以后想直接獲取上下文,直接敲一個(gè)UIGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.設(shè)置繪圖信息(拼接路徑)
UIBezierPath *path = [UIBezierPath bezierPath];
// 設(shè)置起點(diǎn)
[path moveToPoint:CGPointMake(10,10)];
// 添加一條線到某個(gè)點(diǎn)
[path addLineToPoint:CGPointMake(125,125)];
[path addLineToPoint:CGPointMake(240,10)];
// 3.把路徑添加到上下文
// 直接把UIKit的路徑轉(zhuǎn)換成CoreGraphics,CG開頭就能轉(zhuǎn)
CGContextAddPath(ctx, path.CGPath);
// 4.把上下文渲染到視圖
// Stroke描邊
CGContextStrokePath(ctx);
}
- (void)draw2Line
{
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路徑
UIBezierPath *path = [UIBezierPath bezierPath];
// 設(shè)置起點(diǎn)
[path moveToPoint:CGPointMake(10,125)];
// 添加一條線到某個(gè)點(diǎn)
[path addLineToPoint:CGPointMake(230,125)];
//??? // 設(shè)置起點(diǎn)
//??? [path moveToPoint:CGPointMake(10, 10)];
//
//??? // 添加一條線到某個(gè)點(diǎn)
//??? [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);
// 設(shè)置繪圖狀態(tài)
// 設(shè)置線寬
CGContextSetLineWidth(ctx,10);
CGContextSetLineCap(ctx, kCGLineCapRound);
//??? CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
[[UIColor redColor] set];
// 4.渲染上下文到視圖
CGContextStrokePath(ctx);
}

第二種:下載進(jìn)度條:
?- (void)setProgress:(CGFloat)progress
{
_progress = progress;
self.myLabel.text = [NSString stringWithFormat:@"%.2f%%",progress*100];
//??? [self drawRect:self.bounds];
// 重新繪制
// 在view上做一個(gè)重繪的標(biāo)記,當(dāng)下次屏幕刷新的時(shí)候,就會調(diào)用drawRect.
[self setNeedsDisplay];
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
// 當(dāng)視圖顯示的時(shí)候會調(diào)用 默認(rèn)只會調(diào)用一次
- (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(@"隨機(jī)數(shù)--%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;
// 第一個(gè)扇形
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);
// 第二個(gè)扇形
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);
// 第三個(gè)扇形
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);
// 設(shè)置繪圖的狀態(tài)
[[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);
// 把棧頂上下文取出來,替換當(dāng)前上下文
CGContextRestoreGState(ctx);
// 設(shè)置繪圖的狀態(tài)
//??? [[UIColor blackColor] set];
//??? CGContextSetLineWidth(ctx, 1);
//??? CGContextSetLineCap(ctx, kCGLineCapButt);
// 4.渲染
CGContextStrokePath(ctx);
}

每日更新關(guān)注:https://weibo.com/hanjunqiang新浪微博
第七種:矩形操作
?- (void)drawRect:(CGRect)rect
{
// Drawing code
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 注意:你的路徑一定放在上下文矩陣操作之后
// 平移上下文
CGContextTranslateCTM(ctx,50,100);
// 旋轉(zhuǎn)上下文
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);
}
詳細(xì):https://www.2cto.com/kf/201608/542831.html
