前言
??iOS支持兩套圖形API族:Core Graphics和OpenGL ES。OpenGL ES是跨平臺的圖形API,屬于OpenGL的一個簡化版本。而QuartZ 2D是蘋果公司開發的一套API,它是Core Graphics Framewore的一部分。
??本文中主要是對Core Graphics框架的學習總結。
??對于一個初學者來說,UIKit和Core Graphics兩個支持繪圖的框架常常令我們感到迷惑。
UIKit
??UIKit是一組Objective-C API,為線條圖形、Quartz圖像和顏色操作提供Objective-C封裝,并提供2D繪制、圖像處理及用戶接口級別的動畫。
??UIKit包含UIBezierPath(繪制線、角度、橢圓及其它圖形)、UIImage(顯示圖像)、UIColor(顏色操作)、UIFont和UIScreen(提供字體和屏幕信息)等類,它在位圖圖形環境、PDF圖形環境上進行繪制和操作功能,也提供對標準視圖的支持,也提供對打印功能的支持。
??使用UIKit,你只能在當前上下文中繪圖。
Core Graphics
??這是一個繪圖專用的API族,它經常被稱為QuartZ或者QuartZ 2D。Core Graphics 是iOS上所有繪圖功能的基石,包括UIKit(UIKit是對Core Graphics的進一步封裝)。
??使用Core Graphics之前需要指定一個用于繪圖的圖形上下文(CGContextRef),這個圖形上下文會在每個繪圖函數中都會用到。
??至此,我們了解了這兩個框架的基本關系,而這兩個框架中都提到了圖形上下文(圖形場景)。獲得一個圖形上下文是我們完成繪圖任務的第一步,可以將圖形上下文理解為一塊畫布。那么下邊介紹三種獲得圖形上下文的方法:
- (void)drawRect:(CGRect)rect方法
??在此方法中,UIKit框架可以直接繪圖,而Core Graphics可以通過UIGraphicsGetCurrentContext函數獲取當前的圖形上下文。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
??在此方法中,直接傳入了圖形上下文,但是該上下文不一定是當前的圖形上下文,所以在UIKit框架下繪圖時,需要調用UIGraphicsPushContext()函數將該上下文變為當前上下文,結束時調用UIGraphicsPopContext()函數還原上下文。而Core Graphics框架下可以直接使用該上下文。
UIGraphicsBeginImageContextWithOptions函數
??此函數獲取圖形上下文的規則跟drawRect:方法是一樣的。利用該上下文,你就可以在其上進行繪圖,并生成圖片。調用UIGraphicsGetImageFromCurrentImageContext函數可從當前上下文中獲取一個UIImage對象。記住在你所有的繪圖操作后別忘了調用UIGraphicsEndImageContext函數關閉圖形上下文。
??這里主要解釋一下三個參數的含義。第一個參數表示所要創建的圖片尺寸;第二個參數用來指定生成圖片的背景是否為不透明,YES表示不透明,則得到的是黑色的背景;第三個參數制定生成圖片的縮放因子,這個縮放因子與UIImage的scale屬性所指的含義是一致的。0則表示讓圖片的縮放因子根據屏幕的分辨率而變化。
基于兩大繪圖框架和三種獲取圖形上下文的方法的六種繪圖形式
舉例說明:繪制一個藍色的圓
第一種繪圖形式:基于UIKit框架(即使用UIBezierPath)在UIView的子類方法drawRect:中實現。
- (void) drawRect: (CGRect) rect {
UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
}
第二種繪圖形式:基于Core Graphics框架在UIView的子類方法drawRect:中實現。
- (void) drawRect: (CGRect) rect {
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
}
第三種繪圖形式:基于UIKit框架(即使用UIBezierPath) 在UIView的子類方法drawLayer:inContext:中實現。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
[super drawLayer:layer inContext:ctx];
UIGraphicsPushContext(ctx);
UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
UIGraphicsPopContext();
}
PS:同時此方法必須重寫drawRect:方法,否則上邊的方法不會被調用。
詳細了解這塊的調用邏輯可以看這篇文章
iOS的繪圖之drawRect和drawLayer:inContext
第四種繪圖形式: 使用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);
}
??最后,演示UIGraphicsBeginImageContextWithOptions的用法,并從上下文中生成一個UIImage對象。生成UIImage對象的代碼可以在任何地方被使用,它沒有上述繪圖方法那樣的限制。
第五種繪圖形式: 使用UIKit實現:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0);
UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
第六種繪圖形式: 使用Core Graphics實現:
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();
UIGraphicsEndImageContext();
UIKit和Core Graphics可以在相同的圖形上下文中混合使用。在iOS 4.0之前,使用UIKit和UIGraphicsGetCurrentContext被認為是線程不安全的。而在iOS4.0以后蘋果讓繪圖操作在第二個線程中執行解決了此問題。
寫在最后的話
從開始研究這個框架看了很多文章,收獲很多,慢慢從這些文章中一點點的找到它們之間的關系,這個過程是曲折的,但也是喜悅的。后續還會更新,本篇文章只是開始,一點點皮毛。