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