iOS繪圖總結,ios繪圖
前言
開發中有項目用到iOS繪圖,去年一直很忙,沒抽出時間來總結,現在算是對去年項目裡繪圖的一個總結,
一、畫圓的幾種方式前言
iOS支援兩套圖形API族:Core Graphics/QuartZ 2D 和OpenGL ES。OpenGL ES是跨平台的圖形API,屬於OpenGL的一個簡化版本。QuartZ 2D是蘋果公司開發的一套API,它是Core Graphics Framework的一部分。需要注意的是:OpenGL ES是API,該介面描述了方法、結構、函數應具有的行為以及應該如何被使用的語義。也就是說它只定義了一套規範,具體的實現由裝置製造商根據規範去做。而往往很多人對介面和實現存在誤解。舉一個不恰當的比喻:上發條的時鐘和裝電池的時鐘都有相同的可視行為,但兩者的內部實現截然不同。因為製造商可以自由的實現Open GL ES,所以不同系統實現的OpenGL ES也存在著巨大的效能差異。 iOS系統自身提供了兩套繪圖的架構,即UIBezierPath 和 Core Graphics。而前者所屬UIKit,其實是對Core Graphics架構關於path的進一步封裝,所以使用起來比較簡單。但是畢竟Core Graphics更接近底層,所以它更加強大。
Core Graphics
Core Graphics API所有的操作都在一個上下文中進行。所以在繪圖之前需要擷取該上下文並傳入執行渲染的函數中。如果你正在渲染一副在記憶體中的圖片,此時就需要傳入圖片所屬的上下文。獲得一個圖形上下文是我們完成繪圖任務的第一步,你可以將圖形上下文理解為一塊畫布。如果你沒有得到這塊畫布,那麼你就無法完成任何繪圖操作。當然,有許多方式獲得一個圖形上下文,這裡我介紹兩種最為常用的擷取方法。
MyView1
- (void) drawRect: (CGRect) rect { UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; [[UIColor blueColor] setFill]; [p fill];}
MyView2
- (void) drawRect: (CGRect) rect { CGContextRef con = UIGraphicsGetCurrentContext(); CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor); CGContextFillPath(con);}
在UIView子類的drawLayer:inContext:方法中實現繪圖任務。drawLayer:inContext:方法是一個繪製圖層內容的代理方法。為了能夠調用drawLayer:inContext:方法,我們需要設定圖層的代理對象。但要注意,不應該將UIView對象設定為顯示層的委派物件,這是因為UIView對象已經是隱式層的代理對象,再將它設定為另一個層的委派物件就會出問題。輕量級的做法是:編寫負責繪圖形的代理類
MyView3
- (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con { UIGraphicsPushContext(con); UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; [[UIColor orangeColor] setFill]; [p fill]; UIGraphicsPopContext();}
MyView4
- (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con { CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); CGContextSetFillColorWithColor(con, [UIColor orangeColor].CGColor); CGContextFillPath(con);}
建立一個圖片類型的上下文。調用UIGraphicsBeginImageContextWithOptions函數就可獲得用來處理圖片的圖形上下文。利用該上下文,你就可以在其上進行繪圖,並產生圖片。調用UIGraphicsGetImageFromCurrentImageContext函數可從當前上下文中擷取一個UIImage對象。記住在你所有的繪圖操作後別忘了調用UIGraphicsEndImageContext函數關閉圖形上下文。
MyImageView1
- (void) awakeFromNib { [super awakeFromNib]; UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0); CGContextRef con = UIGraphicsGetCurrentContext(); CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); CGContextSetFillColorWithColor(con, [UIColor yellowColor].CGColor); CGContextFillPath(con); UIImage* im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.image = im;}
MyImageView2
- (void) awakeFromNib { [super awakeFromNib]; UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0); UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; [[UIColor yellowColor] setFill]; [p fill]; UIImage* im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.image = im;}
繪圖的步驟總結:1、 擷取上下文 2、 建立路徑(描述路徑) 3、把路徑添加到上下文 4、 渲染上下文關於drawRect方法
1、通常在drawRect裡面繪圖,只有在這個方法裡面才能擷取到跟 View的layer相關聯的圖形上下文
2、drawRect在以下情況下會被調用:
1、如果在UIView初始化時沒有設定rect大小,將直接導致drawRect不被自動調用。drawRect調用是在Controller->loadView, Controller->viewDidLoad 兩方法之後掉用的.所以不用擔心在控制器中,這些View的drawRect就開始畫了.這樣可以在控制器中設定一些值給View(如果這些View draw的時候需要用到某些變數值).
2、該方法在調用sizeToFit後被調用,所以可以先調用sizeToFit計算出size。然後系統自動調用drawRect: 方法。
3、通過設定contentMode屬性值為UIViewContentModeRedraw。那麼將在每次設定或更改frame的時候自動調用drawRect:。
4、直接調用setNeedsDisplay,或者setNeedsDisplayInRect:觸發drawRect:,但是有個前提條件是rect不能為0。
以上1,2推薦;而3,4不提倡
3、drawRect方法使用注意點:
1、若使用UIView繪圖,只能在drawRect:方法中擷取相應的contextRef並繪圖。如果在其他方法中擷取將擷取到一個invalidate的ref並且不能用於畫圖。drawRect:方法不能手動顯示調用,必須通過調用setNeedsDisplay 或者 setNeedsDisplayInRect,讓系統自動調該方法。 2、若使用CAlayer繪圖,只能在drawInContext: 中(類似於drawRect)繪製,或者在delegate中的相應方法繪製。同樣也是調用setNeedDisplay等間接調用以上方法 3、若要即時畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來掉用setNeedsDisplay即時重新整理螢幕