Quartz 2D繪圖 (1)初識


Quartz 2D介紹

  • 什么是Quartz2D ?
    • Quartz 2D是?個二維繪圖引擎,同時支持iOS和Mac系統。
  • Quartz 2D能完成的工作?
    • 繪制圖形 : 線條\三角形\矩形\圓\弧形
    • 繪制文字
    • 繪制\生成圖片(圖像)
    • 讀取\生成PDF
    • 截圖\裁剪圖片
    • 自定義UI控件
  • 其他需要了解的內容 ?
    • Quartz 2D是純C語言的
    • Quartz 2D的API來自于CoreGraphics框架
    • 數據類型和函數基本都是CG作為前綴
      • CGContextRef
      • CGPathRef
      • CGContextStrokePath(ctx)
  • 使用Quartz2D繪圖的基本步驟:
    • 自定義view
    • 重寫drawRect方法
      • 1.獲取圖形上下文
      • 2.創建路徑對象(添加路徑)
      • 3.渲染

繪制基本圖形

  • 繪制一條線段:
    • 獲取圖形上下文
    • 移動到起點
    • 添加另一點
    • 渲染
    // 1.獲取圖形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    
    // 2.添加路徑
    // 起點
    CGContextMoveToPoint(cxtRef, 100, 80);
    // 另外一個點
    CGContextAddLineToPoint(cxtRef, 200, 200);
    
    // 3.渲染
    CGContextStrokePath(cxtRef); // 僅僅畫線
    
  • 線段效果圖</br>
繪制線段.png
  • 繪制2條線段
// 1.獲取圖形上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.添加路徑
// 起點
CGContextMoveToPoint(cxtRef, 50, 50);
// 另外一個點
CGContextAddLineToPoint(cxtRef, 200, 100);
// 第2條線
CGContextAddLineToPoint(cxtRef, 40, 180);
// 3.渲染
CGContextStrokePath(cxtRef);
  • 2條線效果圖</br>
繪制兩條線段.png
注意:CGContextStrokePath(cxtRef);只是進行畫線,CGContextFillPath(cxtRef)會將線圍起來的部分全部畫出
  • 使用CGContextFillPath(cxtRef)的效果</br>
fill的效果.png
  • 繪制三角形
// 1.獲取圖形上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.添加路徑
// 起點
CGContextMoveToPoint(cxtRef, 50, 50);
// 另外一個點
CGContextAddLineToPoint(cxtRef, 200, 50);
// 第2條線
CGContextAddLineToPoint(cxtRef, 125, 180);
// 再向起點連一條線
CGContextAddLineToPoint(cxtRef, 50, 50);
// 3.渲染
CGContextStrokePath(cxtRef); // 僅僅畫線
  • 三角形效果圖</br>
三角形.png

繪制其他基本圖形

通過UIBezierPath繪制基本圖形

注意:如果在繪制一部分圖形后仍需要繪制圖形,必須將前面的路徑關閉,再重新開始繪制。
  • 三角形及線段
    // 1.獲取圖形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    
    // 2.圖形路徑
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(10, 10)];
    [bezierPath addLineToPoint:CGPointMake(200, 200)];
    [bezierPath addLineToPoint:CGPointMake(10, 250)];
    [bezierPath addLineToPoint:CGPointMake(10, 10)];
    
    // 2.1如果需要再繪制其他的圖形就必須先把當前的路徑關閉
    [bezierPath closePath];
    
    // 2.2.再繪制一條線段
    [bezierPath moveToPoint:CGPointMake(250, 10)];
    [bezierPath addLineToPoint:CGPointMake(250, 290)];
    
    // 需要的是CGPathRef類型,可以通過.CGPath進行轉換
    CGContextAddPath(cxtRef, bezierPath.CGPath);
    // 3.渲染
    CGContextDrawPath(cxtRef, kCGPathStroke);
    

* 通過UIBezierPath繪制基本圖形效果圖</br>

![通過bezierPath繪制基本圖形.png](http://upload-images.jianshu.io/upload_images/1988000-57b79968c4183195.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#### 繪制矩形
```objc
// 1.獲取圖形上下文
CGContextRef ctxRef = UIGraphicsGetCurrentContext();

// 2.創建路徑對象
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 200, 100)];

// 3.將路徑對象添加到"圖形上下文"
CGContextAddPath(ctxRef, path.CGPath);

// 4.渲染
CGContextDrawPath(ctxRef, kCGPathStroke);
  • 矩形效果圖</br>
繪制矩形.png

繪制圓角矩形

// 2.創建路徑對象
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 10, 200, 100) cornerRadius:50];
  • 圓角矩形效果圖</br>


    繪制圓角矩形.png

繪制橢圓

// 2.創建路徑對象
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 200, 100)];
  • 橢圓效果圖</br>
繪制橢圓.png

繪制圓弧

  • clockWise參數,代表繪制時YES:是逆時針繪制 NO:是順時針繪制
// 2.創建路徑對象
// 圓心
CGPoint center = CGPointMake(150, 150);
// 半徑
CGFloat radius = 100;
// 開始角度
CGFloat startAngle = 0;
// 結束角度
CGFloat endAngle = M_PI_2;
// 是否為順時針
BOOL clokcWise = NO;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clokcWise];
  • 圓弧效果圖</br>
繪制圓弧.png

繪制圓形

// 圓心
CGPoint center = CGPointMake(150, 150);
// 半徑
CGFloat radius = 100;
// 開始角度
CGFloat startAngle = 0;
// 結束角度
CGFloat endAngle = M_PI * 2;
// 是否為順時針
BOOL clokcWise = YES;
return [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clokcWise];
  • 圓形效果圖</br>
繪制圓形.png

繪制常見圖形

  • 繪制扇形
  • 繪制餅狀圖
  • 繪制柱狀圖
  • 繪制下載進度條

繪制扇形

  • 需要繪制弧形,指定起始角度及結束角度
  • 最后需要將弧形終點與圓心點連起來,再通過填充渲染就可以
    // 1.獲取圖形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    
    // 2.創建弧形的路徑對象
    // 圓心
    CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
    // 半徑
    CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5;
    
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI_4 clockwise:YES];
    
    // 必須要將終點與圓形連線
    [path addLineToPoint:center];
    
    // 4.添加到圖形上下文中
    CGContextAddPath(cxtRef, path.CGPath);
    
    // 5.渲染
    

// CGContextDrawPath(cxtRef, kCGPathStroke);
CGContextDrawPath(cxtRef, kCGPathFill);
```

  • 扇形效果圖</br>


    繪制扇形.png

繪制餅狀圖

  • 數組數據
  • 通過計算每個元素所占的比例
  • 獲取圖形上下文
  • 計算好圓心及半徑
  • 遍歷集合數據進行繪制
  • 計算每個扇形需要旋轉的角度
  • 創建路徑對象進行繪制
注意:在繪制時,每個扇形的終止角度 = 上一個扇形的結束角度 + 當前扇形所需要的角度
每次遍歷最后都需要將扇形的起始角度置為上一個扇形的結束角度
#pragma mark - 繪制餅狀圖
- (void)drawRect:(CGRect)rect {

    // 1.數據
    NSArray *data = @[@25, @15, @10, @5, @30, @10, @5];

    // 2.遍歷數據進行求和
    int total = 0;
    for (NSNumber *number in data) {
        float num = number.floatValue;
        total += num;
    }
    // 3.進行繪圖操作
    // 3.1獲取圖形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 3.2遍歷集合進行繪圖操作
    // 圓心
    CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
    // 半徑
    CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5;

    // 起始角度和結束角度
    // 注意:要在block內修改外部局部變量的值是必須要加__block進行修飾的
    __block CGFloat startAngle = 0;
    __block CGFloat endAngle = 0;

    // 遍歷集合
    [data enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        // 轉為float類型
        float num = obj.floatValue;

        // 計算單獨一個數據結束角度,需要用它所占的角度 + 起始角度
        endAngle = (num / total) * (M_PI * 2) + startAngle;

        // 3.2.1創建路徑對象
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];

        // 3.2.2將弧線的終點與圓心連接起來
        [path addLineToPoint:center];

        // 3.2.3使用隨機色進行渲染
        [[UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0] set];

        // 3.2.4添加到圖形上下文中
        CGContextAddPath(cxtRef, path.CGPath);

        // 3.2.5渲染
        CGContextDrawPath(cxtRef, kCGPathFill);

        // 3.2.6修改起始角度
        startAngle = endAngle;
    }];


    // 4.創建同心圓遮蓋中間部分
    UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius - 70 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
    [self.backgroundColor setFill];

    CGContextAddPath(cxtRef, arcPath.CGPath);

    // 渲染
    CGContextDrawPath(cxtRef, kCGPathFill);

}

  • 餅狀圖效果圖</br>
繪制餅狀圖.png

繪制柱狀圖

  • 計算所占比例
  • 獲取圖形上下文
  • 計算寬度,遍歷集合進行繪制
#pragma mark - 繪制柱狀圖
- (void)drawRect:(CGRect)rect {

    // 1.數據
    NSArray *data = @[@100, @200, @300, @700, @79, @400];

    // 2.遍歷數據進行求和
    float total = 0;
    for (NSNumber *number in data) {
        float num = number.floatValue;
        total += num;
    }

    // 3.進行繪圖操作
    // 3.1獲取圖形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 3.2遍歷集合進行繪圖操作
    // 3.2.1計算寬度
    CGFloat width = rect.size.width / (data.count * 2 - 1);

    [data enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        // 計算高度
        CGFloat height = obj.floatValue / total * rect.size.height * 2;

        // 計算y
        CGFloat y = rect.size.height - height;
        // 計算x
        CGFloat x = (2 * width) * idx;

        // 創建路徑對象
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, width, height)];

        // 添加到圖形上下文中
        CGContextAddPath(cxtRef, path.CGPath);

        // 設置隨機色
        [[UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0] set];

        // 渲染
        CGContextDrawPath(cxtRef, kCGPathFill);
    }];

}
  • 柱狀圖效果圖</br>
繪制柱形圖.png

繪制下載進度條

  • 監聽slider的滑動事件
  • 將slider的值傳遞給自定義view
  • 在自定義view中重寫set方法,進行繪制
  • 繪制時弧線的結束角度根據傳入的進度值進行計算
#pragma mark - 繪制下載進度條
- (void)drawRect:(CGRect)rect {

    // 1.獲取上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.創建路徑對象
    // 圓心
    CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
    // 半徑
    CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5;
    // 起點
    CGFloat startAngle = -M_PI_2;
    CGFloat endAngle = self.progress * M_PI * 2 - M_PI_2;

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];

    [path addLineToPoint:center];

#pragma mark - 創建一個圓形,利用奇偶填充模式留出中間的空白處
//    UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius - 50 startAngle:startAngle endAngle:endAngle clockwise:YES];

//    [path2 addLineToPoint:center];

    // 3.添加到上下文中
    CGContextAddPath(cxtRef, path.CGPath);
//    CGContextAddPath(cxtRef, path2.CGPath);
    // 設置顏色
    [[UIColor brownColor] set];

    // 4.渲染
    CGContextDrawPath(cxtRef, kCGPathFill);
//    CGContextDrawPath(cxtRef, kCGPathEOFill);
}
  • 下載進度條圖效果圖</br>
繪制下載進度條.gif

圖形上下文的矩陣操作

注意: 紅色框為上下文的邊界

旋轉

#pragma mark - 旋轉 CTM
    // 注意:是繞著左上角進行轉動
    CGContextRotateCTM(cxtRef, M_PI_4);
  • 旋轉上下文</br>
旋轉.png

縮放


#pragma mark - 縮放 CTM
    // 注意:是以左上角為圓點縮放
    CGContextScaleCTM(cxtRef, 0.5, 0.5);
  • 縮小上下文</br>
縮放.png

平移

#pragma mark - 平移
    CGContextTranslateCTM(cxtRef, 200, -100); 
  • 平移上下文</br>
平移.png
  • 旋轉縮放平移</br>
旋轉縮放平移.png

圖形上下文棧介紹

  • 在對圖形上下文進行矩陣操作之前先將圖像上下文最初的狀態保存起來。保存的位置就是圖形上下文的棧。
  • 在需要原始狀態的時候進行再回復原始的狀態的。
   
#pragma mark - 矩陣操作
- (void)drawRect:(CGRect)rect {
    
    //獲取圖形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    //MARK: - 在矩陣操作前保存圖形上下文
    CGContextSaveGState(cxtRef);
    
#pragma mark - 旋轉 CTM
    // 注意:是繞著左上角進行轉動
    CGContextRotateCTM(cxtRef, M_PI_4);
    
#pragma mark - 縮放 CTM
    // 注意:是以左上角為圓點縮放
    CGContextScaleCTM(cxtRef, 0.5, 0.5);
    
#pragma mark - 平移
    CGContextTranslateCTM(cxtRef, 200, -100);  //為什么y值為0   和y值為-100   x值也會改變   縮放同時改變位置  位置會不準
    //創建路徑
    //圓形
    UIBezierPath *roundPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(180, 150) radius:100 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
    //矩形
    UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(150, 280, 200, 100)];
    //直線
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    [linePath moveToPoint:CGPointMake(50, 50)];
    [linePath addLineToPoint:CGPointMake(350, 400)];
    
    //添加路徑
    CGContextAddPath(cxtRef, roundPath.CGPath);
    CGContextAddPath(cxtRef, rectPath.CGPath);
    CGContextAddPath(cxtRef, linePath.CGPath);
    
    //渲染
    CGContextDrawPath(cxtRef, kCGPathStroke);
    
    
    //MARK: - 在執行完畢矩陣操作以后,恢復上下文
    CGContextRestoreGState(cxtRef);
    
    //繪制與控制器視圖大小相等的方框方便觀察
    UIBezierPath *rectP = [UIBezierPath bezierPathWithRect:rect] ;
    //設置矩形的顏色
    [[UIColor redColor] setStroke];
    //設置矩形的線寬
    rectP.lineWidth = 10;
    
    [rectP stroke];
    
    
}


圖形上下文的內存管理

  • CoreGraphics框架里面的使用到"create"和"copy","retain"函數創建的對象,最后都需要進行釋放
// 1.獲取上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();

// 2.創建路徑對象
CGMutablePathRef path = CGPathCreateMutable();


// 起點
CGPathMoveToPoint(path, NULL, 50, 50);

// 另一點
CGPathAddLineToPoint(path, NULL, 100, 100);


// 3.將路徑對象添加到圖形上下文中
CGContextAddPath(cxtRef, path);

// 4.渲染
CGContextDrawPath(cxtRef, kCGPathStroke);

// 5.釋放
//    CGPathRelease(path);
#pragma mark - CoreGraphics框架里面的使用到"create"和"copy","retain"函數創建的對象,最后都需要進行釋放
CFRelease(path);

繪制圖片和文字

繪制圖片

//1.加載圖片
     UIImage *mjImage = [UIImage imageNamed:@"頭像"];
     //2.1方式1 通過一個點開始繪制
     [mjImage drawAtPoint:CGPointMake(50, 50)];
通過一個點開始繪制.png
     //2.2方式2 通過某個區域繪制,可能會壓縮或者拉伸圖片,使圖片不夠美觀
     [mjImage drawInRect:CGRectMake(50, 50, 100, 150)];
通過某個區域繪制.png

     //2.3方式3 在某個區域內以平鋪的方式繪制圖片
     [mjImage drawAsPatternInRect:CGRectMake(0, 0, 300, 300)];
在某個區域內以平鋪的方式繪制圖片.png

繪制文字



//字符串
    NSString *str = [NSString stringWithFormat:@"說好的不熬夜呢?"];
    
    NSDictionary *dic = @{
                          NSFontAttributeName : [UIFont systemFontOfSize:20],
                          NSForegroundColorAttributeName : [UIColor redColor]
                          };
    
    //方式1. 從某一個點開始繪制
    [str drawAtPoint:CGPointMake(50, 50) withAttributes:dic];
    

從某一個點開始繪制.png
    //方式2. 在某一個區域進行繪制  (會自動換行,但是區域不要小于字體所占面積大小)
    [str drawInRect:CGRectMake(150, 150, 80, 150) withAttributes:dic];

在某一個區域進行繪制.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Quartz2D以及drawRect的重繪機制字數1487 閱讀21 評論1 喜歡1一、什么是Quartz2D Q...
    PurpleWind閱讀 808評論 0 3
  • 第一步:先科普一下基礎知識: Core Graphics是基于C的API,可以用于一切繪圖操作 Core Grap...
    真愛要有你才完美閱讀 2,476評論 0 1
  • 其實這只是一個小魚缸,因為角度和后期的原因看起來像海水。 對于出生在魚缸里的魚來說,魚缸應該就是它們的全世界,我不...
    阿基尼曼閱讀 405評論 1 1
  • 在鄉間的道路上是一種什么樣的感覺呢?就是一切都很近,人和生活,生活和自然都靠的很近。在這條路上,周圍的一切都很熟悉...
    汪陳閱讀 171評論 0 1
  • 第一次看到這個名字的時候,我想起了《勞拉快跑》,那個為了自己的愛人不停息地奔跑在城市間的身影。 我原以為,電影不過...
    52755閱讀 471評論 0 2