[iOS UI進階 - 0] Quiartz2D

A.簡介
1. 須要掌握的
  • drawRect:方法的使用
  • 常見圖形的繪製:線條、多邊形、圓
  • 繪圖狀態的設置:文字顏色、線寬等
  • 圖形上下文狀態的保存與恢復
  • 圖形上下文棧

1.基本圖形繪製
* 線段(線寬、線段樣式)
* 矩形(空心、實心、顏色)
* 三角形、梯形等形狀
* 橢圓\圓
* 圓弧
* 文字繪製
* 圖片繪製(pattern)
* 圖形上下文棧

2.練習(畫人)
3.模仿UIImageView
4.自定義checkbox
5.圖片裁剪
6.圖片水印
7.條紋背景
8.截圖
 
 
2.概念
Quartz 2D是一個二維繪圖引擎,同時支持iOS和Mac系統
Quartz 2D能完成的工做
  • 繪製圖形 : 線條\三角形\矩形\圓\弧等
  • 繪製文字
  • 繪製\生成圖片(圖像)
  • 讀取\生成PDF
  • 截圖\裁剪圖片
  • 自定義UI控件
  • … …
 
Quartz 2D能作不少強大的事情,例如
  • 裁剪圖片
  • 塗鴉\畫板
  • 手勢解鎖
 
B.Quartz2D在iOS開發中的價值
  • 爲了便於搭建美觀的UI界面,iOS提供了UIKit框架,裏面有各類各樣的UI控件
  • UILabel:顯示文字
  • UIImageView:顯示圖片
  • UIButton:同時顯示圖片和文字(能點擊)
  • … …

利用UIKit框架提供的控件,拼拼湊湊,能搭建和現實一些簡單、常見的UI界面

可是,有些UI界面極其複雜、並且比較個性化,用普通的UI控件沒法實現,這時能夠利用Quartz2D技術將控件內部的結構畫出來,自定義控件的樣子

其實,iOS中大部分控件的內容都是經過Quartz2D畫出來的

所以,Quartz2D在iOS開發中很重要的一個價值是:自定義view(自定義UI控件)
 
C.圖形上下文
圖形上下文(Graphics Context):是一個CGContextRef類型的數據

圖形上下文的做用
保存繪圖信息、繪圖狀態
決定繪製的輸出目標(繪製到什麼地方去?)
(輸出目標能夠是PDF文件、Bitmap或者顯示器的窗口上)
Image(270)
相同的一套繪圖序列,指定不一樣的Graphics Context,就可將相同的圖像繪製到不一樣的目標上
 
D.Quartz2D提供瞭如下幾種類型的Graphics Context
Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context
 
Image(271)
 
E.使用
1.自定義view
如何利用Quartz2D自定義view?(自定義UI控件)

如何利用Quartz2D繪製東西到view上?
首先,得有圖形上下文,由於它能保存繪圖信息,而且決定着繪製到什麼地方去
其次,那個圖形上下文必須跟view相關聯,才能將內容繪製到view上面

自定義view的步驟
新建一個類,繼承自UIView
實現- (void)drawRect:(CGRect)rect方法,而後在這個方法中
取得跟當前view相關聯的圖形上下文
繪製相應的圖形內容
利用圖形上下文將繪製的全部內容渲染顯示到view上面
 
2. drawRect:
爲何要實現drawRect:方法才能繪圖到view上?
由於在drawRect:方法中才能取得跟view相關聯的圖形上下文

drawRect:方法在何時被調用?
當view第一次顯示到屏幕上時(被加到UIWindow上顯示出來)
調用view的setNeedsDisplay或者setNeedsDisplayInRect:時
 
Image(272)
 
3.Quartz2D須知
Quartz2D的API是純C語言的

Quartz2D的API來自於Core Graphics框架


數據類型和函數基本都以CG做爲前綴
CGContextRef
CGPathRef
CGContextStrokePath(ctx);
……
 
4.drawRect:中取得的上下文
在drawRect:方法中取得上下文後,就能夠繪製東西到view上

View內部有個layer(圖層)屬性,drawRect:方法中取得的是一個Layer Graphics Context,所以,繪製的東西實際上是繪製到view的layer上去了

View之因此能顯示東西,徹底是由於它內部的layer
 
5.Quartz2D 繪圖的代碼步驟
得到圖形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

拼接路徑(下面代碼是搞一條線段)
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);

繪製路徑
CGContextStrokePath(ctx); // CGContextFillPath(ctx);
 
6.經常使用拼接路徑函數
新建一個起點
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)

添加新的線段到某個點
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)

添加一個矩形
void CGContextAddRect(CGContextRef c, CGRect rect)

添加一個橢圓
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)

添加一個圓弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
  CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
 
7.經常使用繪製路徑函數
Mode參數決定繪製的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)

繪製空心路徑
void CGContextStrokePath(CGContextRef c)

繪製實心路徑
void CGContextFillPath(CGContextRef c)

提示:通常以CGContextDraw、CGContextStroke、CGContextFill開頭的函數,都是用來繪製路徑的
 
F.小練習
1.畫線段
(1)新建single view application,拖入一個view,建立一個class並設置
7C034679-1E00-4A8F-A576-62B4471DE619
 
(2)在自定義的view class 中實現drawRect:方法
 1 //重寫drawRect:
 2 - (void)drawRect:(CGRect)rect {
 3     // 1.得到圖形上下文
 4     CGContextRef ctx = UIGraphicsGetCurrentContext();
 5    
 6     // 2.拼接圖形
 7     // 2.1設置一個起點
 8     CGContextMoveToPoint(ctx, 10, 10);
 9    
10     // 2.2添加一條線段,是從(10,10)到(100,100)
11     CGContextAddLineToPoint(ctx, 100, 100);
12    
13     // 2.3從上次的位置開始再添加一條線段,是從(100,100)到(150,40)
14     CGContextAddLineToPoint(ctx, 150, 40);
15    
16     // 2.4最後畫一條直線鏈接會原處,造成一個三角形
17 //    CGContextAddLineToPoint(ctx, 10, 10);
18     CGContextClosePath(ctx); // 回到起點
19    
20     // 3.渲染顯示到view上面
21     CGContextStrokePath(ctx);
22 }
 
Image(273)
 
2.畫矩形
 1 - (void)drawRect:(CGRect)rect {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4    
 5     // 2.畫四邊形
 6     CGContextAddRect(ctx, CGRectMake(10, 10, 80, 100));
 7    
 8     // 繪製空心圖形
 9 //    CGContextStrokePath(ctx);
10    
11     // 繪製實心圖形
12     CGContextFillPath(ctx);
13 }
 
Image(274)
 
3.畫圓
 1 - (void) drawRound {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4    
 5     // 2.1畫圓
 6     CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100));
 7    
 8     // 2.2橢圓
 9     CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 150, 100));
10    
11     // 渲染
12     CGContextStrokePath(ctx);
13 }
 
Image(275)
 
 
4.畫圓弧
 1 - (void) drawArc {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4    
 5     // 2.圓弧
 6     // X軸正方向爲0角度,最後一個參數1表明逆時針方向
 7     CGContextAddArc(ctx, 100, 100, 50, 0, -M_PI, 1);
 8    
 9     // 渲染
10     CGContextStrokePath(ctx);
11 }

 

Image(276)
 
1     // 2.圓弧
2     // X軸正方向爲0角度,最後一個參數1表明逆時針方向
3     CGContextAddArc(ctx, 100, 100, 50, 0, -M_PI, 0);

 

Image(277)
 
 
5.畫文字
 1 - (void) drawText {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4    
 5     // 2.畫上文字
 6     /**
 7      * 若是使用已通過期的方法 CGContextShowText,因爲CG畫板是以左下角爲零點,因此字會上下顛倒過來
 8      */
 9     NSString *text = @"hello, 你好啊";
10 //    [text drawAtPoint:CGPointZero withAttributes:nil];
11    
12     CGRect r = CGRectMake(50, 50, 100, 100);
13     CGContextAddRect(ctx, r);
14     CGContextFillPath(ctx);
15    
16     NSMutableDictionary *dict = [NSMutableDictionary dictionary];
17     dict[NSForegroundColorAttributeName] = [UIColor redColor]; // 前景色,就是字體顏色
18     dict[NSFontAttributeName] = [UIFont systemFontOfSize:20]; // 字體
19     [text drawInRect:r withAttributes:dict];
20    
21     // 渲染
22     CGContextStrokePath(ctx);
23 }
 
Image(278)
 
6.畫圖片
(1)使用已經封裝好,不用手動轉換座標的方法
 1 - (void) drawImg {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4    
 5     // 取得圖片
 6     UIImage *img = [UIImage imageNamed:@"M4"];
 7    
 8     // 畫上圖片
 9 //    [img drawAtPoint:CGPointZero]; // 原圖大小,可能顯示不全
10     [img drawInRect:CGRectMake(0, 0, 100, 200)]; // 填充方式默認是拉伸
11    
12     // 渲染
13     CGContextStrokePath(ctx);
14 }
 
(2)填充方式
a.drawAtPoint 默認是原圖
    [img drawAtPoint:CGPointZero]; // 原圖大小,可能顯示不全
Image(279)
 
b.drawInRect 是拉伸
    [img drawInRect:CGRectMake(0, 0, 100, 200)]; // 填充方式默認是拉伸
Image(280)
 
c.drawAsPetternInRect
 1 - (void) drawImg {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4   
 5     // 取得圖片
 6     UIImage *img = [UIImage imageNamed:@"M4Mini"]; // 小圖
 7  
 8     // 畫上圖片
 9     [img drawAsPatternInRect:CGRectMake(0, 0, 200, 200)]; // 重複,能夠用來作花紋
10   
11     // 渲染
12     CGContextStrokePath(ctx);
13 }
 
(3)文字水印
 1 - (void) drawImg {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4    
 5     // 取得圖片
 6     UIImage *img = [UIImage imageNamed:@"M4"]; // 大圖
 7 //    UIImage *img = [UIImage imageNamed:@"M4Mini"]; // 小圖
 8    
 9     // 畫上圖片
10 //    [img drawAtPoint:CGPointZero]; // 原圖大小,可能顯示不全
11     [img drawInRect:CGRectMake(0, 0, 100, 200)]; // 填充方式默認是拉伸
12 //    [img drawAsPatternInRect:CGRectMake(0, 0, 200, 200)]; // 重複,能夠用來作花紋
13    
14     // 文字
15     NSString *text = @"這是一個美女";
16     [text drawInRect:CGRectMake(0, 0, 100, 30) withAttributes:nil];
17    
18     // 渲染
19     CGContextStrokePath(ctx);
20 }

 

Image(281)
 
G.畫一個小黃人
利用基本的圖形描繪一個簡單地小黃人頭像
使用弧線、直線、橢圓
 
H.圖形上下文棧
爲了在更改上下文設置後可以徹底、方便地恢復原來的設置
 1 - (void) contextStackDemo {
 2     // 1.得到上下文
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4    
 5     // 2.存儲上下文
 6     CGContextSaveGState(ctx);
 7    
 8     // 3.設置上下文
 9     CGContextSetLineCap(ctx, kCGLineCapRound);
10     CGContextSetLineWidth(ctx, 10);
11     [[UIColor redColor] set];
12    
13     // 4.畫第一條直線
14     CGContextMoveToPoint(ctx, 10, 10);
15     CGContextAddLineToPoint(ctx, 100, 100);
16    
17     // 渲染
18     CGContextStrokePath(ctx);
19    
20     // 5.恢復上下文
21     CGContextRestoreGState(ctx);
22    
23     // 6.第二條直線
24     CGContextMoveToPoint(ctx, 100, 10);
25     CGContextAddLineToPoint(ctx, 10, 100);
26    
27     // 渲染
28     CGContextStrokePath(ctx);
29 }
 
Image(282)
 
 
I.矩陣操做 裁剪
1.矩陣旋轉、縮放、移動操做
 1 - (void) testCTM {
 2     CGContextRef ctx = UIGraphicsGetCurrentContext();
 3    
 4     CGContextSaveGState(ctx);
 5    
 6     CGContextRotateCTM(ctx, M_PI_4 * 0.3); // 旋轉
 7     CGContextScaleCTM(ctx, 0.5, 0.5); // 縮放
 8     CGContextTranslateCTM(ctx, 100, 0); // 移動
 9    
10     CGContextAddRect(ctx, CGRectMake(10, 10, 100, 100));
11     CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 60, 60));
12    
13     CGContextMoveToPoint(ctx, 200, 100);
14     CGContextAddLineToPoint(ctx, 50, 200);
15    
16     CGContextStrokePath(ctx);
17   
18 }
 
Image(283)
 
2.裁剪
 1 - (void) testClip {
 2     CGContextRef ctx = UIGraphicsGetCurrentContext();
 3    
 4     // 畫一個圓
 5     CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 150, 150));
 6    
 7     // 裁剪
 8     CGContextClip(ctx);
 9    
10     // 加上圖片
11     UIImage *img = [UIImage imageNamed:@"a9ec8a13632762d0092abc3ca2ec08fa513dc619"];
12     [img drawInRect:CGRectMake(0, 0, 150, 150)];
13   
14     CGContextStrokePath(ctx);    
15 }
 
Image(284)
 
J.重繪(刷幀)
setNeedDisplay
1.經過一個滑塊控件來控制一個圓的動態大小
(1)在storyboard中描繪一個view和一個slider
Image(285)
 
(2)拖入slider的值變化事件和view到控制器,並使用自定義類爲view的class
(3)在自定義UIView類中實現drawRect,在適當的地方調用setNeddDisplay重繪
MyView:
 1 - (void)setRadius:(CGFloat)radius {
 2     _radius = radius;
 3    
 4     // 調用重繪/刷幀方法
 5     [self setNeedsDisplay];
 6 }
 7 
 8 // 初始化控件的時候, drawRect只會調用一次
 9 - (void)drawRect:(CGRect)rect {
10     CGContextRef ctx = UIGraphicsGetCurrentContext();
11    
12     CGContextAddArc(ctx, 125, 125, self.radius, M_PI * 2, 0, 1);
13     CGContextFillPath(ctx); // 實心圓
14 }
 
ViewController:
 1 @interface ViewController ()
 2 - (IBAction)onSlideChange:(UISlider *)sender;
 3 @property (weak, nonatomic) IBOutlet MyView *circleView;
 4 
 5 @end
 6 
 7 @implementation ViewController
 8 
 9 - (void)viewDidLoad {
10     [super viewDidLoad];
11     // Do any additional setup after loading the view, typically from a nib.
12 }
13 
14 - (void)didReceiveMemoryWarning {
15     [super didReceiveMemoryWarning];
16     // Dispose of any resources that can be recreated.
17 }
18 
19 - (IBAction)onSlideChange:(UISlider *)sender {
20     self.circleView.radius = sender.value * 100;
21 }
22 @end
 
能夠用滑條控制圓的大小
Image(286)
 
2.下雪動畫
使用定時器 Timer
使用CADisplayLink
 1 - (void)awakeFromNib {
 2     // 添加定時器
 3 //    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];
 4    
 5     // 刷新更快的工具
 6     CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)]; // 建立
 7    
 8     // 添加到消息循環,啓動
 9     [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
10 }
11 
12 - (void)drawRect:(CGRect)rect {
13     self.snowY += 1;
14     if (self.snowY >= self.frame.size.height) {
15         self.snowY = -100;
16     }
17     UIImage *image = [UIImage imageNamed:@"M2Mini"];
18     [image drawAtPoint:CGPointMake(100, self.snowY)];
19 }
 
3C60EE71-2F0B-400C-B383-1215BF964FA3
相關文章
相關標籤/搜索