天天看點

iOS是怎麼"繪畫"的?

iOS是怎麼"繪畫"的?

如果您以前從事其它平台的圖形/界面開發或者遊戲開發,一定知道, 不管上層UI怎麼呈現和響應, 底層必須有一個繪圖引擎. iOS也不例外. 本文詳細介紹了iOS Graphics的用法和相關知識, 希望對您的Coding有幫助.

什麼是繪圖引擎

  • 部落格: http://www.cnblogs.com/jhzhu
  • 郵箱: [email protected]
  • 作者: 知明是以
  • 時間: 2013-12-31

^此部落格需要對

CALayer

UIView

有基本的了解. 可參考部落格談談iOS Animation

繪圖引擎, 通俗來說就好比給你一張紙一支筆和若幹顔色的顔料, 你可以用它來做最基本的圖形繪制.

一個最基本的繪圖引擎包括一下接口:

//Code-1
I1. drawLine()      //繪制任意線條,并支援對線條上色.
I2. drawPath()      //根據路徑繪制形狀,并支援填充顔色.
I3. drawImage()     //繪制圖像(e.g. xxx.jpg, xxx.png)
I4. drawGradient()  //繪制漸變填充
I5. transform()     //矩陣映射變換.
I6. drawText()      //繪制文字
           

不難想象, 有了以上接口, 我們就可以友善的繪制任意想要的圖像.

這裡強調的是友善, 有些接口并不是必須的. 比如說

drawImage()

,我們總可以調用有限次

drawLine()

drawShape()

來繪制任意給定的Image. 但是複雜程度可想而知.

一個繪圖引擎設計的目的就是為了友善上層調用, 是以它會封裝一些最常用和最基本的接口. 以上5個接口就滿足這兩個條件之一. 所謂最常用和最基本并沒有一個明确的定義, 是以不同的繪圖引擎可能會多一些常用接口,但都大同小異.

iOS的繪圖引擎

下面我們就

Code-1

裡提到的接口在iOS平台上做一個介紹.

在哪裡繪制?

如果我們在XCode裡建立一個

UIView

類, 我們會得到以下代碼:

//Code-2
#import "GraphicsViewControllerView.h"
@implementation GraphicsViewControllerView
- (id)initWithFrame:(CGRect)frame
{ 
    self = [super initWithFrame:frame]; 
    if (self) {
        // Initialization code }
    return self; 
}
- (void)drawRect:(CGRect)rect
{
    // Drawing code 
}
@end
           

通常,

drawRect()

都會被注釋起來. 因為, 如果你向

UIView

添加

subView

或者設定

UIView

的顯示相關的屬性(e.g.

backgroundCrolor

)的時候,

UIKit

會自動的把這些參數代表的含義繪制到

CALayer

上去. 也就是說, 一般情況我們并不需要自己來繪制,

UIKit

會自動幫我們完成繪制工作.

但是, 當不添加

subView

, 不設定

UIView

的顯示相關的屬性時, 我們就可以通過重載

drawRect()

來手動繪制圖像了.

Context

A graphical context can be thought of as a canvas, offering an enormous number of properties such as pen color, pen thickness, etc. Given the context, you can start painting straight away inside the drawRect: method, and Cocoa Touch will make sure that the attributes and properties of the context are applied to your drawings. We will talk about this more later, but now, let’s move on to more interesting subjects.

drawText

我們建立一個

UIView

CustomUIView

,如下重載

drawRect()

方法.

建立

CustomUIView

的對象,不設定任何屬性,添加到顯示清單.

//Code-3
- (void)drawRect:(CGRect)rect{ 
    UIFont *font = [UIFont systemFontWithSize:40.f]; 
    NSString *myString = @"Some String";
    [myString drawAtPoint:CGPointMake(40, 180) withFont:font];
}
           

運作, 就可以看到我們沒有添加任何

UITextField

卻顯示了文字~

more:

關于文字繪制的方法還有

drawInRect:withFont:

等幾個方法, 可參考官方文檔.

setColor

我們把

Code-3

中的代碼添加兩行, 變成:

//Code-4
- (void)drawRect:(CGRect)rect{ 
    UIColor* color = [UIColor blueColor];       //create color
    [color set];                                //set color

    UIFont *font = [UIFont systemFontWithSize:40.f]; 
    NSString *myString = @"Some String";
    [myString drawAtPoint:CGPointMake(40, 180) withFont:font];
}
           

就可以看到,文字由黑色變成了藍色.

UIColor

還有兩個方法

setStroke

setFill

分别設定線條顔色和填充顔色. 在後面的章節會用到.

set

方法影響所有前景色.

drawImage

同樣的, 如下重寫

drawRect

方法:

//Code-5
- (void)drawRect:(CGRect)rect{ 
    /* Assuming the image is in your app bundle and we can load it */
    UIImage *xcodeIcon = [UIImage imageNamed:@"filename.png"];
    [xcodeIcon drawAtPoint:CGPointMake(0.0f, 20.0f)];
}
           

我們看到圖像被繪制出來了.

UIImage

還有其他繪制方法:

drawInRect:
drawAsPatternInRect:
drawAtPoint:blendMode:alpha:
drawInRect:blendMode:alpha:
           

方法名已經很清楚的說明了方法的用途. 具體可參考官方文檔

drawLine

這兩個是繪圖引擎裡最基本的, 是以放在一起講述.

//Code-6: drawLine
- (void)drawRect:(CGRect)rect{ 

    /* Step1 設定繪圖顔色 */ 
    [[UIColor brownColor] set];

    /* Step2 擷取當期的畫布: Graphic Context */ 
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    /* Step3 設定線條寬度 */ 
    CGContextSetLineWidth(currentContext,5.0f);

    /* Step4 把畫筆移動到起始點 */ 
    CGContextMoveToPoint(currentContext,50.0f, 10.0f);

    /* Step5 從起始點繪制線條到終點 */ 
    CGContextAddLineToPoint(currentContext,100.0f, 200.0f);

    /* Step6 送出繪制 */
    CGContextStrokePath(currentContext); 

}
           

如果想連續繪制多條線, 可以再

Code-6

中的

Step5

Step6

之間多次調用

CGContextAddLineToPoint()

.

CGContextSetLineJoin

可以改變線條交叉點的樣式.

drawPath

如果我們想快速繪制一條折線, 調用

drawLine

就顯得有些臃腫. 是以有了

drawPath

, 它是

drawLine

的加強版.

drawPath

的一般步驟如下:

//Code-7: drawPath
- (void)drawRect:(CGRect)rect{ 

    /* Step1 擷取當期的畫布: Graphic Context */ 
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    /* Step2 建立 path */ 
    CGMutablePathRef path = CGPathCreateMutable();

    /* Step3 移動到起始點 */
    CGPathMoveToPoint(path,NULL, screenBounds.size.width, screenBounds.origin.y);

    /* Step4 繪制一個橢圓 */
    CGPathAddEllipseInRect(path, &CGAffineTransformIdentity, CGRectMake(0, 320, 320, 160));

    /* Step5 再添加一條直線 */
    CGPathAddLineToPoint(path,NULL, screenBounds.origin.x, screenBounds.size.height);

    /* Step6 向畫布添加path */
    CGContextAddPath(currentcontext, path);

    /* Step7 設定繪制類型: kCGPathStroke(繪制邊緣), kCGPathFill(填充path内區域), kCGPathFillStroke(包含前面兩項)*/
    CGContextDrawPath(currentcontext, kCGPathFillStroke);

    /* Step8 送出繪制 */
    CGContextStrokePath(currentContext); 

    /* Step9 release path */
    CGPathRelease(path);

}
           

常見幾何圖形的繪制接口:

CGPathAddCurveToPoint

,

CGPathAddArcToPoint

CGPathAddRect

等等...

transform

iOS中的

transform

使用

CGAffineTransform

表示的. 你可以用矩陣方式構造任意二維變換:

/*
a: The value at position [1,1] in the matrix.
b: The value at position [1,2] in the matrix.
c: The value at position [2,1] in the matrix.
d: The value at position [2,2] in the matrix.
tx: The value at position [3,1] in the matrix.
ty: The value at position [3,2] in the matrix.
*/
CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)
           

矩陣表示為:

iOS也提供了常見變換的快速建構方式:

CGAffineTransformIdentity           //機關矩陣, 不做任何變換
CGAffineTransformMakeRotation       //旋轉
CGAffineTransformMakeScale          //縮放
CGAffineTransformMakeTranslation    //平移
           

Code-7 Step4

中, 繪制

path

時用到了機關矩陣

CGAffineTransformIdentity

, 表示不做任何變換. 通常情況下, 都是在繪制階段把

transform

作為參數傳入. 上面提到的

CGPathAddCurveToPoint

CGPathAddArcToPoint

CGPathAddRect

函數都有一個

transform

參數.

iOS繪圖在項目中的應用

通常, 我們隻需要随心所欲的對

UIView

增加

subView

UIKit

會自動幫我們繪制. 但是下列情況下可能需要手動繪制:

  1. 優化

    UITableViewCell

    的時候. 如果我們的

    cell

    很複雜, 有很多

    subView

    , 就會變得很卡頓. 就需要手動繪制了. 可參考部落格: 優化UITableView性能或者IOS詳解TableView——性能優化及手工繪制UITableViewCell
  2. 暫未想到, 以後想到再說.

此部落格已遷移至blog.bookbook.in,以後不再更新