iOS-繪圖-CoreGraphics

這一篇寫的繪圖,其實主要寫的是CoreGraphics。下面我們通過一張圖來了解一下CoreGraphics在整個系統(tǒng)的框架中的位置。

我們可以看到它位于Media Layer(Media Layer:媒體層提供應(yīng)用中視聽方面的技術(shù),如圖形圖像相關(guān)的CoreGraphics,CoreImage,GLKit,OpenGL ES,CoreText,ImageIO等等。聲音技術(shù)相關(guān)的CoreAudio,OpenAL,AVFoundation,視頻相關(guān)的CoreMedia,Media Player框架,音視頻傳輸?shù)腁irPlay框架等)。

一、系統(tǒng)的繪圖框架都有哪些

系統(tǒng)繪圖框架
  • UIKit
    我們平常最常用的就是UIKit,其底層是依賴CoreGraphics實現(xiàn)的,而且絕大多數(shù)的圖形界面也都是由UIKit完成,并且UIImage、NSString、UIBezierPath、UIColor等都知道如何繪制自己,也提供了一些方法來滿足我們常用的繪圖需求。
  • CoreGraphics
    主要的繪圖系統(tǒng),常用于繪制自定義視圖,純C的API,使用Quartz2D做引擎。Core Graphics數(shù)據(jù)結(jié)構(gòu)和函數(shù)可以通過CG前綴來識別。
  • Core Animation
    提供了強大的2D和3D動畫服務(wù),它也與UIView高度集成。
  • Core Image
    提供了非??斓膱D片過濾方式,比如模糊,切圖,銳化,扭曲和其他一些你能想象的變形效果。
  • OpenGL-ES
    主要用于游戲繪制,但它是一套編程規(guī)范,具體由設(shè)備制造商實現(xiàn)。

二、繪圖方式

1. 繪圖周期 (個人理解還不透徹,會繼續(xù)學(xué)習(xí)更新)

首先我們需要了解繪圖周期,因為都是在繪圖周期中進(jìn)行的。

  • iOS會在運行循環(huán)中整合所有繪圖請求,并在RunLoop將要結(jié)束時,一次將他們繪制出來。

所以,不能在子線程中繪制,也不能進(jìn)行過于復(fù)雜的操作(否則會造成主線程卡頓)。

2. 繪圖方式
  • 視圖繪制
    調(diào)用UIView的drawRect方法進(jìn)行繪制。如果調(diào)用一個view的setNeedsDisplay方法,那么該視圖會被標(biāo)記為需要重新繪制,會在下個繪制周期中重新繪制(此時會自動調(diào)用drawRect方法)。

drawRect被觸發(fā):
1.會在第一次被add到父視圖上;2.還有調(diào)用setNeedsDisplay。

  • 視圖布局
    調(diào)用UIView的layoutSubviews進(jìn)行布局。如果調(diào)用一個view的setNeedsLayout方法,那么該view會被標(biāo)記為需要重新布局,UIKit會自動調(diào)用layoutSubviews方法及其子視圖的layoutSubviews方法。

在繪圖時,我們應(yīng)該多使用布局,少使用繪制,因為布局使用的是GPU,繪制使用的是CPU。GPU對于圖形處理有優(yōu)勢,CPU要處理的事情較多,且不擅長圖形處理。

三、準(zhǔn)備工作

在介紹具體方法之前,我們需要知道,iOS的繪圖必須在上下文中繪制,所以繪制前必須先獲取上下文。如果是繪制圖片,則先獲取一個圖片上下文,如果是其他視圖,就需要獲取一個非圖片上下文。上下文可以理解為畫布,在上面進(jìn)行繪圖。

1.上下文
  • context(在drawRect方法中獲?。?br> 圖形上下文(注意不是圖片),可以通過UIGraphicsGetCurrentContext獲取。
  • imageContext (不必在drawRect方法中)
    圖片上下文,通過UIGraphicsBeginImageContextWithOptions:獲取一個圖片上下文,然后繪制完成后,調(diào)用UIGraphicsGetImageFromCurrentImageContext獲取繪制的圖片,最后要記得關(guān)閉圖片上下文UIGraphicsEndImageContext。

四、具體繪圖方法

由于iOS常見的繪圖框架有兩種,所以繪圖的方法也有多種,我們介紹幾種常見的方法。

1.通過圖片類型的上下文

圖片類型的上下文,不需要在drawRect方法中,在普通的oc方法中就可以進(jìn)行繪制:
使用CoreGraphics實現(xiàn):

// 獲取圖片上下文
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();
// 關(guān)閉圖片上下文
UIGraphicsEndImageContext();

使用UIKit方式實現(xiàn)

// 獲取圖片上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0);
// 繪圖
UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
// 從圖片上下文中獲取繪制的圖片
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉圖片上下文
UIGraphicsEndImageContext();
2.通過drawRect:方法

在view的drawRect方法中,實現(xiàn)重新繪制:
使用CoreGraphics實現(xiàn):

- (void) drawRect: (CGRect) rect {
    CGContextRef con = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
    CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
    CGContextFillPath(con);
}

使用UIKit方式實現(xiàn)

- (void) drawRect: (CGRect) rect {
    UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
    [[UIColor blueColor] setFill];
    [p fill];
}
3.通過drawLayer:inContext:

待續(xù)。。。

五、CoreGraphics具體如何使用

上邊內(nèi)容大多是告訴我們CoreGraphics的使用場景以及系統(tǒng)相關(guān)內(nèi)容介紹,下邊我們來看看具體CoreGraphics是如何使用的

1.具體步驟:
  • 先在drawRect方法中獲得上下文context(或通過imageContext);
  • 繪制圖形(線,圖形,圖片等);
  • 設(shè)置一些修飾屬性;
  • 渲染到上下文,完成繪圖。
    show me code
#import "CustomView.h"

@implementation CustomView

- (void)drawRect:(CGRect)rect
{
    // 1.獲取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    // --------------------------實心圓
    
    // 2.畫圖
    CGContextAddEllipseInRect(ctx, CGRectMake(10, 10, 50, 50));
    [[UIColor greenColor] set];
    
    // 3.渲染
    CGContextFillPath(ctx);
    
    
    
    // --------------------------空心圓
    
    CGContextAddEllipseInRect(ctx, CGRectMake(70, 10, 50, 50));
    [[UIColor redColor] set];
    CGContextStrokePath(ctx);
    
    
    
    // --------------------------橢圓
    //畫橢圓和畫圓方法一樣,橢圓只是設(shè)置不同的長寬
    CGContextAddEllipseInRect(ctx, CGRectMake(130, 10, 100, 50));
    [[UIColor purpleColor] set];
    CGContextFillPath(ctx);
    
    
    
    // --------------------------直線
    CGContextMoveToPoint(ctx, 20, 80); // 起點
    CGContextAddLineToPoint(ctx, self.frame.size.width-10, 80); //終點
//    CGContextSetRGBStrokeColor(ctx, 0, 1.0, 0, 1.0); // 顏色
    [[UIColor redColor] set]; // 兩種設(shè)置顏色的方式都可以
    CGContextSetLineWidth(ctx, 2.0f); // 線的寬度
    CGContextSetLineCap(ctx, kCGLineCapRound); // 起點和重點圓角
    CGContextSetLineJoin(ctx, kCGLineJoinRound); // 轉(zhuǎn)角圓角
    CGContextStrokePath(ctx); // 渲染(直線只能繪制空心的,不能調(diào)用CGContextFillPath(ctx);)
    
    
    
    // --------------------------三角形
    CGContextMoveToPoint(ctx, 10, 150); // 第一個點
    CGContextAddLineToPoint(ctx, 60, 100); // 第二個點
    CGContextAddLineToPoint(ctx, 100, 150); // 第三個點
    [[UIColor purpleColor] set];
    CGContextClosePath(ctx);
    CGContextStrokePath(ctx);
    
    
    
    // --------------------------矩形
    CGContextAddRect(ctx, CGRectMake(20, 170, 100, 50));
    [[UIColor orangeColor] set];
//    CGContextStrokePath(ctx); // 空心
    CGContextFillPath(ctx);
    
    
    
    // --------------------------圓弧
    CGContextAddArc(ctx, 200, 170, 50, M_PI, M_PI_4, 0);
    CGContextClosePath(ctx);
    CGContextFillPath(ctx);
    
    
    // --------------------------文字
    NSString *str = @"你在紅樓,我在西游";
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[NSForegroundColorAttributeName] = [UIColor whiteColor]; // 文字顏色
    dict[NSFontAttributeName] = [UIFont systemFontOfSize:14]; // 字體
    
    [str drawInRect:CGRectMake(20, 250, 300, 30) withAttributes:dict];
    

    // --------------------------圖片
    UIImage *img = [UIImage imageNamed:@"yingmu"];
//    [img drawAsPatternInRect:CGRectMake(20, 280, 300, 300)]; // 多個平鋪
//    [img drawAtPoint:CGPointMake(20, 280)]; // 繪制到指定點,圖片有多大就顯示多大
    [img drawInRect:CGRectMake(20, 280, 80, 80)]; // 拉伸
}

六、UIBezierPath

而前者所屬UIKit,其實是對Core Graphics框架關(guān)于path的進(jìn)一步封裝,所以使用起來比較簡單。但是畢竟Core Graphics更接近底層,所以它更加強大。

UIBezierPath可以創(chuàng)建基于矢量的路徑,例如橢圓或者矩形,或者有多個直線和曲線段組成的形狀。
使用UIBezierPath,你只能在當(dāng)前上下文中繪圖。
1.所以如果你當(dāng)前處于UIGraphicsBeginImageContextWithOptions函數(shù)或drawRect:方法中,你就可以直接使用UIKit提供的方法進(jìn)行繪圖。
2.如果你持有一個context:參數(shù),那么使用UIKit提供的方法之前,必須將該上下文參數(shù)轉(zhuǎn)化為當(dāng)前上下文。幸運的是,調(diào)用UIGraphicsPushContext 函數(shù)可以方便的將context:參數(shù)轉(zhuǎn)化為當(dāng)前上下文,記住最后別忘了調(diào)用UIGraphicsPopContext函數(shù)恢復(fù)上下文環(huán)境。

它的繪圖的步驟是這樣的:

  • 重寫drawRect方法。但不需要我們自己獲取當(dāng)前上下文context;
  • 創(chuàng)建相應(yīng)圖形的UIBezierPath對象,并設(shè)置一些修飾屬性;
  • 渲染,完成繪制。
1.繪制多邊形
- (void)drawRect:(CGRect)rect
{
    UIColor *color = [UIColor colorWithRed:0 green:0.7 blue:0 alpha:1];
    //[[UIColor redColor] setFill];//設(shè)置填充色
    //[[UIColor redColor] setStroke];//設(shè)置線條
    [color set];//同時設(shè)置線條顏色和填充顏色
    
    UIBezierPath* aPath = [UIBezierPath bezierPath];
    aPath.lineWidth = 5.0;
    
    aPath.lineCapStyle = kCGLineCapRound;
    aPath.lineJoinStyle = kCGLineCapRound;
    
    // 起點
    [aPath moveToPoint:CGPointMake(100.0, 0.0)];
    
    // 繪制線條
    [aPath addLineToPoint:CGPointMake(200.0, 40.0)];
    [aPath addLineToPoint:CGPointMake(160, 140)];
    [aPath addLineToPoint:CGPointMake(40.0, 140)];
    [aPath addLineToPoint:CGPointMake(0.0, 40.0)];
    [aPath closePath];//第五條線通過調(diào)用closePath方法得到的
    
    //根據(jù)坐標(biāo)點連線
    [aPath stroke];
    [aPath fill];
}

//橢圓
UIBezierPath* aPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, 100, 50)];
//矩形
UIBezierPath* aPath = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, 100, 50)];
2.圓弧
- (void)drawRect:(CGRect)rect
{
    UIColor *color = [UIColor redColor];
    [color set]; //設(shè)置線條顏色
    
    UIBezierPath* aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(80, 80)
                                                         radius:75
                                                     startAngle:0
                                                       endAngle:DEGREES_TO_RADIANS(135)
                                                      clockwise:YES];
    
    aPath.lineWidth = 5.0;
    aPath.lineCapStyle = kCGLineCapRound; //線條拐角
    aPath.lineJoinStyle = kCGLineCapRound; //終點處理
    
    [aPath stroke];
}
3.曲線

兩種曲線:

  1. 一個control point


- (void)drawRect:(CGRect)rect
{
    UIColor *color = [UIColor redColor];
    [color set]; //設(shè)置線條顏色
    
    UIBezierPath* aPath = [UIBezierPath bezierPath];
    
    aPath.lineWidth = 5.0;
    aPath.lineCapStyle = kCGLineCapRound; //線條拐角
    aPath.lineJoinStyle = kCGLineCapRound; //終點處理
    
    [aPath moveToPoint:CGPointMake(20, 100)];
    
    [aPath addQuadCurveToPoint:CGPointMake(120, 100) controlPoint:CGPointMake(70, 0)];
    
    [aPath stroke];
}

2.兩個control point


- (void)drawRect:(CGRect)rect
{
    UIColor *color = [UIColor redColor];
    [color set]; //設(shè)置線條顏色
    
    UIBezierPath* aPath = [UIBezierPath bezierPath];
    
    aPath.lineWidth = 5.0;
    aPath.lineCapStyle = kCGLineCapRound; //線條拐角
    aPath.lineJoinStyle = kCGLineCapRound; //終點處理
    
    [aPath moveToPoint:CGPointMake(5, 80)];
    
    [aPath addCurveToPoint:CGPointMake(155, 80) controlPoint1:CGPointMake(80, 0) controlPoint2:CGPointMake(110, 100)];
    
    [aPath stroke];
}

參考:
iOS繪圖框架CoreGraphics分析
iOS的不同繪圖系統(tǒng)
繪圖(具體使用)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作為繪圖引擎。它提供了低...
    ShanJiJi閱讀 1,721評論 0 20
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,187評論 8 265
  • Stumbling on the lawn, is the white tower. Built in one d...
    Z__plus閱讀 287評論 0 0
  • 估計對于大多數(shù)人來說,大學(xué)不僅僅是一個學(xué)習(xí)平臺,更是一個大膽嘗試曾經(jīng)不敢嘗試事情的平臺。戀愛是一段奇妙的約會過程,...
    鴻漁閱讀 1,320評論 6 21
  • 暑假還沒有結(jié)束我就拖著行李箱來到了六百公里之外的學(xué)校實習(xí),未來十天的課程實習(xí)不長,但是今天一天的奔波讓我想到了相當(dāng)...
    _Alieness_閱讀 286評論 1 1

友情鏈接更多精彩內(nèi)容