iOS 繪圖

iOS繪圖教程 
Core Graphics Framework是一套基於C的API框架,使用了Quartz作爲繪圖引擎。它提供了低級別、輕量級、高保真度的2D渲染。該框架可以用於基於路徑的繪圖、變換、顏色管理、脫屏渲染,模板、漸變、遮蔽、圖像數據管理、圖像的創建、遮罩以及PDF文檔的創建、顯示和分析。

iOS支持兩套圖形API族:Core Graphics/QuartZ 2D 和OpenGL ES。OpenGL ES是跨平臺的圖形API,屬於OpenGL的一個簡化版本。QuartZ 2D是蘋果公司開發的一套API,它是Core Graphics Framework的一部分。需要注意的是:OpenGL ES是應用程序編程接口,該接口描述了方法、結構、函數應具有的行爲以及應該如何被使用的語義。也就是說它只定義了一套規範,具體的實現由設備製造商根據規範去做。而往往很多人對接口和實現存在誤解。舉一個不恰當的比喻:上發條的時鐘和裝電池的時鐘都有相同的可視行爲,但兩者的內部實現截然不同。因爲製造商可以自由的實現Open GL ES,所以不同系統實現的OpenGL ES也存在着巨大的性能差異。

第一種方法就是創建一個圖片類型的上下文。調用UIGraphicsBeginImageContextWithOptions函數就可獲得用來處理圖片的圖形上下文。利用該上下文,你就可以在其上進行繪圖,並生成圖片。調用UIGraphicsGetImageFromCurrentImageContext函數可從當前上下文中獲取一個UIImage對象。記住在你所有的繪圖操作後別忘了調用UIGraphicsEndImageContext函數關閉圖形上下文。

第二種方法是利用cocoa爲你生成的圖形上下文。當你子類化了一個UIView並實現了自己的drawRect:方法後,一旦drawRect:方法被調用,Cocoa就會爲你創建一個圖形上下文,此時你對圖形上下文的所有繪圖操作都會顯示在UIView上。

判斷一個上下文是否爲當前圖形上下文需要注意的幾點: 
1.UIGraphicsBeginImageContextWithOptions函數不僅僅是創建了一個適用於圖形操作的上下文,並且該上下文也屬於當前上下文。 
2.當drawRect方法被調用時,UIView的繪圖上下文屬於當前圖形上下文。 
3.回調方法所持有的context:參數並不會讓任何上下文成爲當前圖形上下文。此參數僅僅是對一個圖形上下文的引用罷了。

UIKit和Core Graphics兩個支持繪圖的框架 
UIKit

像UIImage、NSString(繪製文本)、UIBezierPath(繪製形狀)、UIColor都知道如何繪製自己。這些類提供了功能有限但使用方便的方法來讓我們完成繪圖任務。一般情況下,UIKit就是我們所需要的。 
使用UiKit,你只能在當前上下文中繪圖,所以如果你當前處於UIGraphicsBeginImageContextWithOptions函數或drawRect:方法中,你就可以直接使用UIKit提供的方法進行繪圖。如果你持有一個context:參數,那麼使用UIKit提供的方法之前,必須將該上下文參數轉化爲當前上下文。幸運的是,調用UIGraphicsPushContext 函數可以方便的將context:參數轉化爲當前上下文,記住最後別忘了調用UIGraphicsPopContext函數恢復上下文環境。


Core Graphics

這是一個繪圖專用的API族,它經常被稱爲QuartZ或QuartZ 2D。Core Graphics是iOS上所有繪圖功能的基石,包括UIKit。 
使用Core Graphics之前需要指定一個用於繪圖的圖形上下文(CGContextRef),這個圖形上下文會在每個繪圖函數中都會被用到。如果你持有一個圖形上下文context:參數,那麼你等同於有了一個圖形上下文,這個上下文也許就是你需要用來繪圖的那個。如果你當前處於UIGraphicsBeginImageContextWithOptions函數或drawRect:方法中,並沒有引用一個上下文。爲了使用Core Graphics,你可以調用UIGraphicsGetCurrentContext函數獲得當前的圖形上下文。 
至此,我們有了兩大繪圖框架的支持以及三種獲得圖形上下文的方法(drawRect:、drawRect: inContext:、UIGraphicsBeginImageContextWithOptions)。那麼我們就有6種繪圖的形式。

1.第一種繪圖形式:在UIView的子類方法drawRect:中繪製一個藍色橢圓,使用UIKit在Cocoa爲我們提供的當前上下文中完成繪圖任務。

- (void)drawRecWithBezierPath {

    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 0, 60, 70)];
    [[UIColor blueColor] setStroke];
    [path stroke];
}

2.第二種繪圖形式:使用Core Graphics實現繪製藍色圓。

- (void) drawRect: (CGRect) rect { 

CGContextRef con = UIGraphicsGetCurrentContext(); 
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); 
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor); 
CGContextFillPath(con); 

} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.第三種繪圖形式:我將在UIView子類的drawLayer:inContext:方法中實現繪圖任務。drawLayer:inContext:方法是一個繪製圖層內容的代理方法。爲了能夠調用drawLayer:inContext:方法,我們需要設定圖層的代理對象。但要注意,不應該將UIView對象設置爲顯示層的委託對象,這是因爲UIView對象已經是隱式層的代理對象,再將它設置爲另一個層的委託對象就會出問題。輕量級的做法是:編寫負責繪圖形的代理類。在MyView.h文件中聲明如下代碼:

@interface MyLayerDelegate : NSObject <CALayerDelegate>

@end

然後MyView.m文件中實現接口代碼:

@implementation MyLayerDelegate

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {

    UIGraphicsPushContext(ctx); // 將ctx轉化爲當前上下文
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, 80, 80)];
    [[UIColor blueColor] setStroke];
    [path stroke];
    UIGraphicsPopContext(); // 恢復上下文環境
}

@end

直接將代理類的實現代碼放在MyView.m文件的#import代碼的下面,這樣感覺好像在使用私有類完成繪圖任務(雖然這不是私有類)。需要注意的是,我們所引用的上下文並不是當前上下文,所以爲了能夠使用UIKit,我們需要將引用的上下文轉變成當前上下文。

因爲圖層的代理是assign內存管理策略,那麼這裏就不能以局部變量的形式創建MyLayerDelegate實例對象賦值給圖層代理。這裏選擇在MyView.m中將其設爲一個屬性:

 

@interface MyView ()
@property (nonatomic, strong) MyLayerDelegate *layerDelegate;

@end

重寫initWithFrame:方法

- (instancetype)initWithFrame:(CGRect)frame {

    if (self = [super initWithFrame:frame]) {
        CALayer *myLayer = [CALayer layer];
        self.layerDelegate = [[MyLayerDelegate alloc] init];
        myLayer.delegate = self.layerDelegate;
        myLayer.frame = self.bounds;
        [self.layer addSublayer:myLayer];

        [myLayer setNeedsDisplay];

    }
    return self;
}

4.第四種繪圖形式: 使用Core Graphics在drawLayer:inContext:方法中實現同樣操作,代碼如下:

- (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con { 

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); 
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor); 
CGContextFillPath(con); 

} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
最後,演示UIGraphicsBeginImageContextWithOptions的用法,並從上下文中生成一個UIImage對象。生成UIImage對象的代碼並不需要等待某些方法被調用後或在UIView的子類中才能去做。

5.第五種繪圖形式: 使用UIKit實現:

- (void)drawImage {

    // UIGraphicsBeginImageContextWithOptions: 第一個參數表示所要創建的圖片的尺寸;第二個參數用來指定所生成圖片的背景是否爲不透明,如上我們使用YES而不是NO,則我們得到的圖片背景將會是黑色,顯然這不是我想要的;第三個參數指定生成圖片的縮放因子,這個縮放因子與UIImage的scale屬性所指的含義是一致的。傳入0則表示讓圖片的縮放因子根據屏幕的分辨率而變化,所以我們得到的圖片不管是在單分辨率還是視網膜屏上看起來都會很好。
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), YES, 0);
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];
    [[UIColor cyanColor] setStroke];
    [path stroke];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    self.imageView.image = image;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6.第六種繪圖形式: 使用Core Graphics實現:

- (void)drawImageWithCoreGraphics {

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);

    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(contextRef, CGRectMake(0, 0, 100, 100));
    [[UIColor orangeColor] setStroke];
    CGContextStrokePath(contextRef);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    self.imageView.image = image;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章