背景:
某天,你正在上班ing……這個時候老大過來了,“小明啊,這個按鈕太丑了,客戶要求變成五角星形狀的,UI沒空給你切圖,你給我完成這個需求啊”,說完便頭也不回的走了,留下你一個人在冷風中搖曳,心里一萬只草泥馬在狂奔……
OK OK ,自己選的iOS開發,哭著也要完成…… 這里你可能就需要使用繪圖這一塊的知識了。如果你恰好這塊還是比較空白的,那么就是。。。
沒關系,先讓我們重drawRect,這個方法開始吧。
首先了解一下這個東西干嘛的,怎么用,啥時候用。
- 1 干嘛的:如果你要對View進行繪制,那么你就要到這里處理
- 2 怎么用:那當然是在這里涂鴉
- 3 啥時候用:代碼說明一切
-[DrawLineViewcontroller loadView]
-[DrawLineViewcontroller viewDidLoad]
-[DrawLineViewcontroller viewWillAppear:]
-[DrawLineView drawRect:]
-[DrawLineViewcontroller viewDidAppear:]
從上述代碼我們看到他是在視圖將要顯示之后和顯示之前調用的。
通常我們需要進行一下的步驟進行繪制。
- 1 獲取當前的上下文(這里只能獲取一次,并且只能在drawRect方法中獲取)
- 2 描述路徑、形狀(就是處理想要顯示的樣子)
- 3 把描述好的路徑、形狀添加早上下文中
- 4 顯示上下文內容
1 畫線
- 第一步:先創建一個工程
- 第二步:創建一個View的子類,因為要重寫drawRect方法
- 第三步:重寫DrawRect
-
第四步:先了解一下線段是如何繪制的,額……兩個點確定一條直線,這些東西大家都懂,所以我們需要一個起點和一個終點來確定一條直線。
3.png
代碼如下:
- (void)drawRect:(CGRect)rect {
// Drawing code
NSLog(@"%s",__func__);
//1.獲取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.描述路徑
UIBezierPath * path = [UIBezierPath bezierPath];
//起點
[path moveToPoint:CGPointMake(10, 10)];
//終點
[path addLineToPoint:CGPointMake(100, 100)];
//設置顏色
[[UIColor whiteColor]setStroke];
//3.添加路徑
CGContextAddPath(contextRef, path.CGPath);
//顯示路徑
CGContextStrokePath(contextRef);
}
額外屬性:CGContextSetLineWidth(contextRef, 5); //設置線寬
1 畫矩形
- 第一步:先創建一個工程
- 第二步:創建一個View的子類,因為要重寫drawRect方法
- 第三步:重寫DrawRect
-
第四步:先了解一下矩形是如何繪制的,矩形是由四個邊組成,所以我們畫四條線段便可以畫一個矩形。
5.png
方法一:
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.獲取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.描述路徑
UIBezierPath * path = [UIBezierPath bezierPath];
//起點
[path moveToPoint:CGPointMake(10, 10)];
//第二個點
[path addLineToPoint:CGPointMake(100, 10)];
//第三個點
[path addLineToPoint:CGPointMake(100, 100)];
//第四個點
[path addLineToPoint:CGPointMake(10, 100)];
//閉合路徑 也等于 [path addLineToPoint:CGPointMake(10, 10)];
[path closePath];
//設置顏色
[[UIColor greenColor]setStroke];
//3.添加路徑
CGContextAddPath(contextRef, path.CGPath);
//顯示路徑
CGContextStrokePath(contextRef);
}
效果圖
方法二:通過一個起點和寬高,可計算出來矩形的大小位置
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.獲取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.描述路徑
UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 100, 100)];
//設置顏色
[[UIColor greenColor]set];
//3.添加路徑
CGContextAddPath(contextRef, path.CGPath);
//顯示路徑
CGContextFillPath(contextRef);
}
效果圖:
畫矩形總結:stroke:描邊 、 fill:填充
//設置描邊顏色
[[UIColor greenColor]setStroke];
//顯示描邊路徑
CGContextStrokePath(contextRef);
//設置填充顏色
[[UIColor greenColor]set];
//顯示填充路徑
CGContextFillPath(contextRef);
3 畫圓
- 第一步:先創建一個工程
- 第二步:創建一個View的子類,因為要重寫drawRect方法
- 第三步:重寫DrawRect
- 第四步:先了解一下圓形是如何繪制的,要確定圓心、半徑,以及旋轉的角度。
圓
方法一:
- (void)drawRect:(CGRect)rect {
// Drawing code
//1、獲取當前上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.描述路徑
//ArcCenter:中心點
//radius:半徑
//startAngle:起始角度
//endAngle:結束角度
//clockwise:是否逆時針
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5) radius:self.bounds.size.width*0.4 startAngle:0 endAngle:M_PI*2 clockwise:NO];
//3.添加路徑到上下文
CGContextAddPath(contextRef, path.CGPath);
//4.設置顏色
[[UIColor brownColor]setFill];
//4.顯示上下文 顯示一個實心圓
// CGContextFillPath(contextRef);
//顯示一個空心圓,描邊
CGContextStrokePath(contextRef);
}
效果圖:
方法二:通過話橢圓的方式去畫圓,大家都知道圓就是橢圓的一個特殊存在。寬高一致的時候就是圓形。
- (void)drawRect:(CGRect)rect {
// Drawing code
//1、獲取當前上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.描述路徑 這是畫橢圓的方法,大家都知道
UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 100, 100)];
//3.添加路徑到上下文
CGContextAddPath(contextRef, path.CGPath);
//4.設置顏色
[[UIColor redColor]setFill];
//4.顯示上下文
CGContextFillPath(contextRef);
}
4 畫文字
- 第一步:先創建一個工程
- 第二步:創建一個View的子類,因為要重寫drawRect方法
- 第三步:重寫DrawRect
- 第四步:寫一段文字,把文字繪制到上下文中
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.獲取當前上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.創建文字
NSString * str = @"紙巾藝術";
//會知道上下文
[str drawInRect:rect withAttributes:nil];
CGContextStrokePath(contextRef);
}
復雜版:
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.獲取當前上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.創建文字
NSString * str = @"紙巾藝術";
//設置字體樣式
NSMutableDictionary * dict = [NSMutableDictionary dictionary];
//NSFontAttributeName:字體大小
dict[NSFontAttributeName] = [UIFont systemFontOfSize:25];
//字體前景色
dict[NSForegroundColorAttributeName] = [UIColor blueColor];
//字體背景色
dict[NSBackgroundColorAttributeName] = [UIColor redColor];
//字體陰影
NSShadow * shadow = [[NSShadow alloc]init];
//陰影偏移量
shadow.shadowOffset = CGSizeMake(2, 2);
//陰影顏色
shadow.shadowColor = [UIColor greenColor];
//高斯模糊
shadow.shadowBlurRadius = 5;
dict[NSShadowAttributeName] = shadow;
//字體間距
dict[NSKernAttributeName] = @10;
//繪制到上下文
//從某一點開始繪制 默認 0 0點
// [str drawAtPoint:CGPointMake(100, 100) withAttributes:nil];
//繪制區域設置
[str drawInRect:rect withAttributes:dict];
//添加到上下文
CGContextStrokePath(contextRef);
}
5 畫圖片
- 第一步:先創建一個工程
- 第二步:創建一個View的子類,因為要重寫drawRect方法
- 第三步:重寫DrawRect
- 第四步:加載一張圖片,繪制到上下文中
<br>
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.獲取當前的上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.加載圖片
//這里順便咯嗦一句:使用imageNamed加載圖片是會有緩存的
//我們這里只需要加載一次就夠了,不需要多次加載,所以不應該保存這個緩存
// UIImage * image = [UIImage imageNamed:@"222"]; //所以可以換一種方式去加載
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"222.png" ofType:nil]];
// //繪制的大小位置
// [image drawInRect:rect];
// //從某個點開始繪制
// [image drawAtPoint:CGPointMake(0, 0)];
//繪制一個多大的圖片,并且設置他的混合模式以及透明度
//Rect:大小位置
//blendModel:混合模式
//alpha:透明度
[image drawInRect:rect blendMode:kCGBlendModeNormal alpha:1];
//從某一點開始繪制圖片,并設置混合模式以及透明度
//point:開始位置
//blendModel:混合模式
//alpha:透明度
// [image drawAtPoint:CGPointMake(0, 0) blendMode:kCGBlendModeNormal alpha:1];
//添加到上下文
CGContextFillPath(contextRef);
}
6 綜合實例:
1 重繪
-
實現一個下載進度條
- 第一步:先創建一個工程
- 第二步:創建一個View的子類,因為要重寫drawRect方法
- 第三步:重寫DrawRect
- 第四步:加載一張圖片,繪制到上下文中
- (void)drawRect:(CGRect)rect {
CGFloat startA = - M_PI_2;
CGFloat endA = - M_PI_2 + self.progress * M_PI * 2;
NSLog(@"%f - %f - %f",self.progress,(3.14159265359*self.progress)/180,endA);
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5) radius:self.bounds.size.width*0.5-10 startAngle:startA endAngle:endA clockwise:YES];
[path stroke];
}
這里我們可以看到我并沒有獲取當前的上下文,這里是因為[path stroke]; 已經幫我們完成了其余的操作。
其實內部實現還是一樣的!!
基本版效果:
高能版:上面的只是完成了功能,但是卻不好看!!然后我們稍稍的美化一下
- (void)drawRect:(CGRect)rect {
CGFloat startA = - M_PI_2;
CGFloat endA = - M_PI_2 + self.progress * M_PI * 2;
NSLog(@"%f - %f - %f",self.progress,(3.14159265359*self.progress)/180,endA);
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5) radius:self.bounds.size.width*0.5-10 startAngle:startA endAngle:endA clockwise:YES];
[[UIColor colorWithRed:self.progress green:(1-self.progress) blue:0 alpha:1]setStroke];
path.lineWidth = 5;
[path stroke];
}
2 不同顏色的線
先跑出問題!
這種線怎么處理!我們先實現三條線
- (void)drawRect:(CGRect)rect {
// Drawing code
UIBezierPath * path = [UIBezierPath bezierPath];
//設置線寬
path.lineWidth = 5;
//第一條線
[[UIColor purpleColor]setStroke];
[path moveToPoint:CGPointMake(10, 10)];
[path addLineToPoint:CGPointMake(10, 100)];
//第二條線
[[UIColor orangeColor]setStroke];
[path moveToPoint:CGPointMake(30, 10)];
[path addLineToPoint:CGPointMake(30, 100)];
//第三條線
[[UIColor greenColor]setStroke];
[path moveToPoint:CGPointMake(50, 10)];
[path addLineToPoint:CGPointMake(50, 100)];
//使用描邊方式添加到上下文中
[path stroke];
}
你可能想當然的就這么做了,可是你發現效果是這樣子的!!!
其實是這樣子的
所以,最后顯示的就是綠色!!!!
這里我們還要了解一個東西!
上下文的狀態棧。
方法一:
- (void)drawRect:(CGRect)rect {
// Drawing code
//1 獲取上下文
//分別設置線段的顏色
CGContextRef purple = UIGraphicsGetCurrentContext();
[[UIColor purpleColor]setStroke];
CGContextSaveGState(purple);
CGContextRef orange = UIGraphicsGetCurrentContext();
[[UIColor orangeColor]setStroke];
CGContextSaveGState(orange);
CGContextRef green = UIGraphicsGetCurrentContext();
[[UIColor greenColor]setStroke];
CGContextSaveGState(green);
UIBezierPath * path = [UIBezierPath bezierPath];
//設置線寬
path.lineWidth = 5;
//把紫色的上下文從棧中取出來
CGContextRestoreGState(purple);
//第一條線
[[UIColor purpleColor]setStroke];
[path moveToPoint:CGPointMake(10, 10)];
[path addLineToPoint:CGPointMake(10, 100)];
[path stroke];
//把紫色的上下文從棧中取出來
CGContextRestoreGState(orange);
path = [UIBezierPath bezierPath];
//設置線寬
path.lineWidth = 9;
//第二條線
[[UIColor orangeColor]setStroke];
[path moveToPoint:CGPointMake(30, 10)];
[path addLineToPoint:CGPointMake(30, 100)];
[path stroke];
//把紫色的上下文從棧中取出來
CGContextRestoreGState(green);
path = [UIBezierPath bezierPath];
//設置線寬
path.lineWidth = 3;
//第三條線
[[UIColor greenColor]setStroke];
[path moveToPoint:CGPointMake(50, 10)];
[path addLineToPoint:CGPointMake(50, 100)];
[path stroke];
}
方法二:
- (void)drawRect:(CGRect)rect {
// Drawing code
[[self bezierPathWithPoint:CGPointMake(10, 10) endPoint:CGPointMake(10, 180) lineColor:[UIColor purpleColor] lineWidth:6] stroke];
[[self bezierPathWithPoint:CGPointMake(50, 10) endPoint:CGPointMake(50, 180) lineColor:[UIColor greenColor] lineWidth:6] stroke];
[[self bezierPathWithPoint:CGPointMake(90, 10) endPoint:CGPointMake(90, 180) lineColor:[UIColor orangeColor] lineWidth:6] stroke];
}
- (UIBezierPath *)bezierPathWithPoint:(CGPoint)startPoint endPoint:(CGPoint) endPoint lineColor:(UIColor*)lineColor lineWidth:(CGFloat)lineWidth{
UIBezierPath * path = [UIBezierPath bezierPath];
[lineColor setStroke];
path.lineWidth = lineWidth;
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
return path;
}
2017年12月11日
新增漸變色的繪制:方案一使用CGContextDrawLinearGradient去做
新建一個View在Controller中加載他,然后實現如下代碼,注釋比較詳細,就不在說明了。
- (void)drawRect:(CGRect)rect {
//畫線
[self drawLine];
//畫一個漸變色的線
[self drawLineWithStartColor:[UIColor redColor] endColor:[UIColor greenColor]];
}
- (void)drawLineWithStartColor:(UIColor *)startColor endColor:(UIColor *)endColor{
//1、獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//3、創建一個顏色空間
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//4、設置顏色的location
CGFloat locations[] = { 0.0, 1.0 };
//5、創建一個顏色數組,由于NSArray里面只能存對象,所以需要橋接
NSArray *colors = @[(__bridge id)startColor.CGColor,(__bridge id)endColor.CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
//釋放
CFRelease(colorSpace);
//保存一份當前上下文,壓棧
CGContextSaveGState(ctx);
//繪制一個矩形,矩形足夠小,也可以是一條線
CGFloat width = self.frame.size.width;
CGContextMoveToPoint(ctx, 0, 120);
CGContextAddLineToPoint(ctx, width, 120);
CGContextAddLineToPoint(ctx, width, 123);
CGContextAddLineToPoint(ctx, 0, 123);
//裁剪
CGContextEOClip(ctx);
//繪制漸變
CGContextDrawLinearGradient(ctx, gradient, CGPointMake(0, 100), CGPointMake(self.frame.size.width, 100), kCGGradientDrawsBeforeStartLocation);
//恢復一份當前的上下文,出棧
CGContextRestoreGState(ctx);
//釋放內存
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
- (void)drawLine{
//1、獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2畫一條直線的需要知道的就是兩個點,也就是起點和終點
//2.1、選擇一個點最為畫線的起點
CGContextMoveToPoint(ctx, 0, 100);
//2.2、設置線的終點
CGContextAddLineToPoint(ctx, 300, 100);
CGContextSetLineWidth(ctx, 3);
//3、填充路徑 填充路徑的方法有兩個 一個是 stroke(描邊) 一個 fill(填充)
CGContextStrokePath(ctx);// 類似于描邊
//4、釋放內存
CGContextRelease(ctx);
}
方案二使用CAGradientLayer去做
CAGradientLayer * layer = [CAGradientLayer layer];
//設置大小
layer.frame = CGRectMake(0, 400, 300, 3);
//設置顏色
layer.colors = @[(__bridge id)[UIColor colorWithRed:204.0 / 255.0 green:224.0 / 255.0 blue:244.0 / 255.0 alpha:1].CGColor,
(__bridge id)[UIColor colorWithRed:29.0 / 255.0 green:156.0 / 255.0 blue:215.0 / 255.0 alpha:1].CGColor,
(__bridge id)[UIColor colorWithRed:0.0 / 255.0 green:50.0 / 255.0 blue:126.0 / 255.0 alpha:1].CGColor];
//漸變層的相對位置,起始點為0,終止點為1,中間點為 (point-startpoint)/(endpoint-startpoint)
layer.locations = @[@0,@.5,@1];
//漸變方向
layer.startPoint = CGPointMake(0, 1);
layer.endPoint = CGPointMake(1, 1);
[self.view.layer addSublayer:layer];