一右锨、繪制基本圖形
1括堤、Quartz 2D是一個二維繪圖引擎,同時支持iOS和Mac系統(tǒng)(Quartz 2D是封裝好一套繪圖的庫绍移,里面放有很多繪圖框架提供使用)
Quartz 2D在ios開發(fā)中很重要的價值就是自定義view(自定義UI控件)
Quartz 2D能完成的工作:
1>繪制圖形 : 線條\三角形\矩形\圓\弧等
2>繪制文字
3>繪制\生成圖片(圖像)
4>讀取\生成PDF
5>截圖\裁剪圖片
6>自定義UI控件(最大的作用自定義view)
為了便于搭建美觀的UI界面悄窃,iOS提供了UIKit框架,里面有各種各樣的UI控件
UILabel:顯示文字
UIImageView:顯示圖片
UIButton:同時顯示圖片和文字(能點擊)
… …
利用UIKit框架提供的控件蹂窖,拼拼湊湊轧抗,能搭建和現(xiàn)實一些簡單、常見的UI界面
但是瞬测,有些UI界面極其復雜横媚、而且比較個性化,用普通的UI控件無法實現(xiàn)月趟,這時可以利用Quartz2D技術(shù)將控件內(nèi)部的結(jié)構(gòu)畫出來灯蝴,自定義控件的樣子
其實,iOS中大部分控件的內(nèi)容都是通過Quartz2D畫出來的
因此孝宗,Quartz2D在iOS開發(fā)中很重要的一個價值是:自定義view(自定義UI控件)
Quartz 2D能做很多強大的事情,例如
裁剪圖片
涂鴉\畫板
手勢解鎖
2穷躁、圖形上下文(Graphics Context):是一個CGContextRef類型的數(shù)據(jù)
圖形上下文的作用
保存繪圖信息、繪圖狀態(tài)
圖形上下文類型不同決定繪制的輸出目標(繪制到什么地方去碳褒?)
(輸出目標可以是PDF文件折砸、Bitmap或者顯示器的窗口上)
相同的一套繪圖序列看疗,指定不同的Graphics Context,就可將相同的圖像繪制到不同的目標上
Quartz2D提供了以下幾種類型的Graphics Context:
Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context
如何利用Quartz2D自定義view睦授?(自定義UI控件)
如何利用Quartz2D繪制東西到view上两芳?
首先,得有圖形上下文去枷,因為它能保存繪圖信息怖辆,并且決定著繪制到什么地方去
其次,那個圖形上下文必須跟view相關(guān)聯(lián)删顶,才能將內(nèi)容繪制到view上面
以下實現(xiàn)的是Layer Graphics Context類型的:
自定義view的步驟:
1>新建一個類竖螃,繼承自UIView
2>實現(xiàn)- (void)drawRect:(CGRect)rect方法(此方法中默認自動生成一個與view相關(guān)聯(lián)的圖形上下文)
3>在這個方法中取得跟當前view相關(guān)聯(lián)的圖形上下文
4>繪制相應的圖形內(nèi)容
5>利用圖形上下文將繪制的所有內(nèi)容渲染顯示到view上面
為什么要實現(xiàn)drawRect:方法才能繪圖到view上?
因為在drawRect:方法中才能取得跟view相關(guān)聯(lián)的圖形上下文
drawRect:方法在什么時候被調(diào)用逗余?
當view第一次顯示到屏幕上時調(diào)用(被加到UIWindow上顯示出來)
調(diào)用view的setNeedsDisplay或者setNeedsDisplayInRect:時
Quartz2D的API是純C語言的特咆,Quartz2D的API來自于Core Graphics框架
數(shù)據(jù)類型和函數(shù)基本都以CG作為前綴
CGContextRef
CGPathRef
CGContextStrokePath(ctx);
在drawRect:方法中取得上下文后,就可以繪制東西到view上
具體實現(xiàn)步驟:
1>自定義View
2>在view的根類中實現(xiàn)- (void)drawRect:(CGRect)rect 方法
3>在方法中=>
- (void)drawRect:(CGRect)rect
{
(1)畫線
//獲取圖形上下文(獲取录粱,創(chuàng)建上下文腻格,都以UIGraphics開頭)
CGContextRef context = UIGraphicsGetCurrentContext();
//繪制路徑
UIBezierPath *path = [UIBezierPath bezierPath];
//設(shè)置起點
[path moveToPoint:CGPointMake(50, 50)];
//添加一根線到終點
[path addLineToPoint:CGPointMake(100, 100)];
//設(shè)置線的顏色
[[UIColor redColor] set];
//設(shè)置線的寬度
CGContextSetLineWidth(context, 20);
//把繪制的內(nèi)容添加到上下文當中
CGContextAddPath(context, path.CGPath);
//把上下文的內(nèi)容顯示到View上(渲染到View的layer)
CGContextStrokePath(context);
//CGContextFillPath(context);
(2)曲線
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 100)];
//controlPoint控制點
[path addQuadCurveToPoint:CGPointMake(100, 100) controlPoint:CGPointMake(50, 30)];
CGContextAddPath(ctx, path.CGPath);
CGContextStrokePath(ctx);
(3)畫三角形
CGContextRef context = UIGraphicsGetCurrentContext();
//繪制路徑
UIBezierPath *path = [UIBezierPath bezierPath];
//設(shè)置起點
[path moveToPoint:CGPointMake(50, 50)];
//添加一根線到終點,并且以終點作為起點再畫一條線
[path addLineToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(50, 200)];
//設(shè)置線的顏色
[[UIColor redColor] set];
//線的冒冒設(shè)置為圓角
CGContextSetLineCap(context, kCGLineCapRound);
//設(shè)置線的寬度
CGContextSetLineWidth(context, 5);
//把繪制的內(nèi)容添加到上下文當中
CGContextAddPath(context, path.CGPath);
//終點與起點閉合
CGContextClosePath(context);
//把上下文的內(nèi)容顯示到View上(渲染到View的layer)
CGContextStrokePath(context);
//CGContextFillPath(context);(Fill有填充及自動關(guān)閉路徑的功能)
(4)畫矩形
1>//獲取圖形上下文(獲取菜职,創(chuàng)建上下文,都以UIGraphics開頭)
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 50, 100)];(寬高一樣就是正方形)
2>只寫下面方法能快速填充出一個矩形
[[UIColor redColor]set];
UIRectFill(CGRectMake(50, 50, 50, 50));
(5)圓角矩形(如果當前的矩形是正方形設(shè)置圓角半徑等于寬畫成圓了旗闽,如果不是正方形酬核,設(shè)置圓角半徑等于寬畫成橢圓)
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 100, 100) cornerRadius:20];
[[UIColor redColor]set];
CGContextAddPath(ctx, path.CGPath);
//CGContextStrokePath(ctx);
CGContextFillPath(ctx);(填充)
(6)畫橢圓(寬高一樣,畫成圓)
//注意:以后使用以下的寫法适室,可以省列很多步驟
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 100, 50)];
[path stroke];//(該方法中封裝了以下步驟:1嫡意、獲取上下文 2、描述路徑 3亭病、把路徑添加到上下文 4鹅很、把上下文的內(nèi)容渲染到view上)
[path fill];(填充)//跟以上同有封裝功能
(7)畫弧
//參數(shù)Center:弧所在的圓心
//參數(shù)radius:圓的半徑
//參數(shù)startAngle:開始的角度
//參數(shù)endAngle:截止角度
//參數(shù)clockwise:YES順時針 NO:逆時針
//注意:不能直接調(diào)用父類的self.center,是因為self.center坐標是相對于它的父控件罪帖,不能使用促煮,必須要使用自定義view的坐標,drawRect方法提供的參數(shù)rect就是自定義view的范圍
CGPoint center =CGPointMake(rect.size.width *0.5, rect.size.height *0.5);
CGFloat radius =rect.size.width *0.5 - 10;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:M_PI_2 endAngle:-M_PI_2 clockwise:YES];
[path stroke];
(8)扇形
//參數(shù)Center:弧所在的圓心
//參數(shù)radius:圓的半徑
//參數(shù)startAngle:開始的角度
//參數(shù)endAngle:截止角度
//參數(shù)clockwise:YES順時針 NO:逆時針
CGPoint center =CGPointMake(rect.size.width *0.5, rect.size.height *0.5);
CGFloat radius =rect.size.width *0.5 - 10;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:- M_PI endAngle:-M_PI_2 clockwise:YES];
[path addLineToPoint:center];
[[UIColor redColor]set];
// [path closePath];
// [path stroke];
[path fill];
(9)畫文字
NSString *str = @"我愛您sdgsdgsdgfdbfdsfagsdgsg";
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
dictM[NSFontAttributeName] = [UIFont systemFontOfSize:50 weight:20];
dictM[NSForegroundColorAttributeName] = [UIColor redColor];
//描邊
dictM[NSStrokeWidthAttributeName] = @3;
dictM[NSStrokeColorAttributeName] = [UIColor blueColor];
//設(shè)置陰影
NSShadow *shadow = [[NSShadow alloc]init];
shadow.shadowColor = [UIColor orangeColor];
shadow.shadowOffset = CGSizeMake(1, 2);
dictM[NSShadowAttributeName] = shadow;
//[str drawAtPoint:CGPointMake(0, 0) withAttributes:dictM];
[str drawInRect:rect withAttributes:dictM];//此方法會自動換行
(10)畫圖片
UIImage *image = [UIImage imageNamed:@"people_wangxinling"];
// UIRectClip(CGRectMake(0, 0, 50, 50));(裁剪,必須在繪制之前進行設(shè)置)
// [image drawInRect:rect];(把藥繪制的圖片給填充給定的區(qū)域當中)
// [image drawAtPoint:CGPointMake(0, 0)];//(繪制的是原始圖片的大小)
[image drawAsPatternInRect:rect];(平鋪)
(11)餅圖(在自定義view的跟類中實現(xiàn))
- (void)drawRect:(CGRect)rect {
NSArray *arr = @[@25,@25,@50];
CGPoint center = CGPointMake(rect.size.width *0.5, rect.size.height *0.5);
CGFloat radius = rect.size.width *0.5 - 10;
CGFloat startA = 0;
CGFloat angle = 0;
CGFloat endA = 0;
for (NSNumber *num in arr) {
startA = endA;
angle = num.intValue/100.0 *M_PI *2;
endA = startA + angle;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius: radius startAngle:startA endAngle:endA clockwise:YES];
[[self radamColor]set];
[path addLineToPoint:center];
[path fill];
}
}
- (UIColor *)radamColor
{
CGFloat r = arc4random_uniform(256)/255.0;
CGFloat g = 0;
CGFloat b = 0;
while ( r == g) {
g = arc4random_uniform(256)/255.0;
}
while ( r == g == b) {
b = arc4random_uniform(256)/255.0;
}
return [UIColor colorWithRed:r green:g blue:b alpha:1.0];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self setNeedsDisplay];
}
3整袁、下載進度條例子實現(xiàn)
//%在stringWithFormat有特殊含義菠齿,不能直接使用,如果想要使用用兩個%代表一個%
self.LabelSlider.text = [NSString stringWithFormat:@"%.2f%%", sender.value];
(2)注意:- (void)drawRect:方法如果是手動調(diào)用的話坐昙,它是不會給你創(chuàng)建view相關(guān)聯(lián)的上下文绳匀,只有系統(tǒng)調(diào)用該方法時,才會創(chuàng)建跟view相關(guān)聯(lián)的上下文,所以一般使用以下方法疾棵,讓系統(tǒng)自動調(diào)用- (void)drawRect:方法
[self setNeedsDisplay];
4戈钢、新的定時器(不使用NSTimer,在自定義view的跟類中實現(xiàn))
- (void)awakeFromNib
{
[super awakeFromNib];
//舊的定時器
//[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(changeY) userInfo:nil repeats:YES];
//新的定時器
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(changeY)];
//想要讓CADisplayLink讓它工作是尔,必須要把他添加到主運行循環(huán)當中
//當每一次屏幕刷新的時候就會調(diào)用指定的方法(屏幕每一秒刷新60次)
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
CGFloat cc = 0;
- (void)changeY
{
cc += 10;
if (cc > self.bounds.size.height) {
cc = 0;
}
//setNeedsDisplay會調(diào)用drawRect:方法殉了,但是它并不是立馬調(diào)用,只是設(shè)了一個表示拟枚,當下一次屏幕刷新的時候才會調(diào)用drawRect:方法薪铜,所以與上面新的定時器同步跑時看起來比較順滑
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed:@"people_wangxinling"];
[image drawInRect:CGRectMake(0, cc, 50, 50)];
}
5、圖形上下文棧(面試題)
(1)//一執(zhí)行下面的方法恩溅,就會獲取跟view相關(guān)聯(lián)的上下文隔箍,在內(nèi)存中分配存儲空間
//上下文在內(nèi)存中的結(jié)構(gòu)分為上下兩個區(qū)域:1>上面區(qū)域是保存路徑,2>下面區(qū)域是保存上下文的狀態(tài)(默認此區(qū)域的線寬為1脚乡,顏色為黑色)
CGContextRef ctx = UIGraphicsGetCurrentContext();
(2)//描述路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 20)];
[path addLineToPoint:CGPointMake(60, 60)];
//以下代碼對上下文狀態(tài)區(qū)域的狀態(tài)進行修改
CGContextSetLineWidth(ctx, 10);
[UIColor redColor] set];
(3)//把路徑添加到上下文中(當執(zhí)行完這行代碼蜒滩,就把線添加到上下文保存路徑的區(qū)域)
CGContextAddPath(ctx, path.CGPath);
//
(4)//把上下文中的內(nèi)容渲染到view當中
當執(zhí)行次操作:會到上下文內(nèi)存的保存路徑的區(qū)域取出全部內(nèi)容,然后把保存在上下文狀態(tài)區(qū)域里的狀態(tài)設(shè)置給取出的內(nèi)容
CGContextStrokePath(ctx);
CGContextFillPath(ctx);
補一://保存當前上下文的狀態(tài)(當執(zhí)行此代碼每窖,就把上下文狀態(tài)區(qū)域里的狀態(tài)復制一份保存到狀態(tài)棧中)
CGContextSaveGState(ctx);
補二://從上下文狀態(tài)棧當中恢復上下文的狀態(tài)(執(zhí)行此代碼帮掉,取出上下文狀態(tài)棧中最上面的狀態(tài)來進行設(shè)值,并且恢復上下文狀態(tài)區(qū)域的狀態(tài)為上下文狀態(tài)棧中最上面的狀態(tài))
CGContextRestoreGState(ctx);
6窒典、圖形上下文的矩陣操作
CGContextTranslateCTM(ctx, 10, 10);
//旋轉(zhuǎn)(最常用)
CGContextRotateCTM(ctx, M_PI);
//縮放
CGContextScaleCTM(ctx, 1.5, 1.5);
7、圖片加水印(是生成一張新的圖片稽莉,在任何地方都可實現(xiàn)瀑志,不是在view上畫東西,所以不需要在 - (void)drawRect:(CGRect)rect實現(xiàn))
位圖上下文需要手動去開啟污秆,開啟多大的上下文劈猪,生成的圖片就多大
//0、加載圖片
UIImage *image = [UIImage imageNamed:@"people_caoying"];
//1良拼、開啟一個跟圖片原始大小的上下文
//參數(shù):opaque不透明
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//2.把圖片繪制到上下文當中
[image drawAtPoint:CGPointZero];
//3.把文字繪制到上下文當中
NSString *str = @"x阿貝";
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
dictM[NSFontAttributeName] = [UIFont systemFontOfSize:100 weight:20];
dictM[NSForegroundColorAttributeName] = [UIColor redColor];
[str drawAtPoint:CGPointMake(10,20) withAttributes:dictM];
//4.從上下文當中生成一張新的圖片
UIImage *newimage = UIGraphicsGetImageFromCurrentImageContext();
//5.關(guān)閉上下文
UIGraphicsEndPDFContext();
self.imageV.image = newimage;
Mode參數(shù)決定繪制的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
繪制空心路徑
void CGContextStrokePath(CGContextRef c)
繪制實心路徑
void CGContextFillPath(CGContextRef c)
提示:一般以CGContextDraw战得、CGContextStroke、CGContextFill開頭的函數(shù)庸推,都是用來繪制路徑的
將當前的上下文copy一份,保存到棧頂(那個棧叫做”圖形上下文棾U欤”)
void CGContextSaveGState(CGContextRef c)
將棧頂?shù)纳舷挛某鰲?替換掉當前的上下文
void CGContextRestoreGState(CGContextRef c)