Core Graphics架構,coregraphics架構
在iOS中常用的架構是Quartz 2D,它是Core Graphics架構的一部分,是一個強大的二維映像繪製引擎。我們日常開發所用到的UIKit的組件都是由Core Graphics架構進行繪製的。當我們匯入UIKit架構時,會自動匯入Core Graphics架構。
在iOS中繪圖一般分為以下幾個步驟:
1.擷取繪圖上下文
2.建立並設定路徑
3.將路徑添加到上下文
4.設定內容相關的狀態
5.繪製路徑
6.釋放路徑
圖形上下文中CGContextRef代表圖形輸出裝置,也就是繪製的位置,包含繪製圖形的一些裝置資訊,Quartz 2D中的所有對象,都最終都必須要添加到圖形上下文,這樣一來,我們在繪製圖形的時候就不必關係具體的裝置資訊。
基本圖形繪製
在UIKit中,已經預設為我們準備好一個圖形內容物件,在UIView的drawrect方法中,我們可以通過通過UIKit封裝函數UIGraphicsGetCurrentContext()方法擷取到圖形上下文(註:在其它方法中無法擷取),然後只需要按照繪製圖形的步驟一步步執行即可,下面重寫view的drawrect方法,在view上添加兩條線:
override func draw(_ rect: CGRect) { // 1.擷取圖形內容物件 let contextRef = UIGraphicsGetCurrentContext() // 2.建立路徑對象 let path = CGMutablePath() path.move(to: CGPoint(x: 10, y: 10)) path.addLine(to: CGPoint(x: 100, y: 100)) // 這一段代碼是繪製曲線,lengths 表示的是 虛線的每段長度為5,每段間距為1,phase 如果填2,則表示虛線的第一個線段的長度為 5 - 2 let a : CGFloat = 5 let b : CGFloat = 1 let lengths = [a,b] contextRef?.setLineDash(phase: 2, lengths: lengths) path.move(to: CGPoint(x: 5, y: 20)) path.addLine(to: CGPoint(x: 100, y: 100)) // 3.添加到映像上下文 contextRef?.addPath(path) // 4.設定圖形上下文狀態屬性 contextRef?.setStrokeColor(UIColor.red.cgColor) // 邊框顏色 contextRef?.setFillColor(UIColor.blue.cgColor) // 填充顏色,可以在封閉路徑中使用 contextRef?.setLineWidth(2) // 線條寬度 // 5.繪製 contextRef?.strokePath() }
封閉圖形繪製
上面的繪圖方式顯得有些麻煩,其實Core Graphics內部對建立路徑對象添加到上下文這兩步操作進行了封裝,可以一步完成,下面我們用線段繪製一個簡單的矩形:
// 1.擷取映像上下文 let contextRef = UIGraphicsGetCurrentContext() // 2.開始繪製一個正方形 contextRef?.move(to: CGPoint(x: 10, y: 10)) contextRef?.addLine(to: CGPoint(x: 110, y: 10)) contextRef?.addLine(to: CGPoint(x: 110, y: 110)) contextRef?.addLine(to: CGPoint(x: 10, y: 110)) // 第四條線我們可以直接使用這個方法繪製 contextRef?.closePath() // 3.設定相關狀態 contextRef?.setLineWidth(2) contextRef?.setStrokeColor(UIColor.red.cgColor) contextRef?.setFillColor(UIColor.blue.cgColor) // 4.開始繪製 .既有邊框(路徑),又有填充 contextRef?.drawPath(using: .fillStroke)
繪製一個矩形
上面的代碼已經相對來說簡化了不少,除了路徑之外,矩形,橢圓也都有相應的繪製方法。
// ----------- 使用addRect方法繪製 ------------ // 1.擷取映像上下文 let contextRef = UIGraphicsGetCurrentContext() // 2.路徑 contextRef?.addRect(CGRect(x: 10, y: 10, width: 100, height: 100)) // 3.狀態 contextRef?.setFillColor(UIColor.blue.cgColor) contextRef?.setStrokeColor(UIColor.red.cgColor) // 4.繪製 contextRef?.drawPath(using: .fillStroke)
// ----------- 使用UI方法繪製 ------------ // 繪製一個填充色為紅色的矩形 UIColor.red.setFill() UIRectFill(CGRect(x: 10, y: 10, width: 100, height: 100)) // 繪製一個邊框為藍色的矩形 UIColor.blue.setStroke() UIRectFrame(CGRect(x: 20, y: 20, width: 100, height: 100))
繪製一個圓
// 1.擷取圖形上下文 let contextRef = UIGraphicsGetCurrentContext() // 2.先規定一個矩形 let rect = CGRect(x: 10, y: 10, width: 100, height: 100) // 3.添加內切圓 contextRef?.addEllipse(in: rect) // 內切圓 // 4.設定填充色 contextRef?.setFillColor(UIColor.red.cgColor) // 5.繪製 contextRef?.drawPath(using: .fill)
繪製一個弧形
// 1.擷取圖形上下文 let contextRef = UIGraphicsGetCurrentContext() // 2.添加弧度 let center = CGPoint(x: 100, y: 100) // 圓心座標
contextRef.moveTo(center) // 加上這句代碼就可以繪製出一個扇形 let radius : CGFloat = 50 // 半徑 let startAngle : CGFloat = 0 // 開始弧度制 let endAngle : CGFloat = CGFloat(Double.pi/4) // 結束弧度制 let clockwise = true // 是否逆時針 contextRef?.addArc(center: center, radius: CGFloat(radius), startAngle: startAngle, endAngle: endAngle, clockwise: clockwise) // 3.設定填充色 contextRef?.setFillColor(UIColor.red.cgColor) // 4.繪製 contextRef?.drawPath(using: .fill)
漸層色填充
在上面的Demo中我們可以看到如何設定填充顏色,事實上很多時候純色的填充並不能滿足我們的足球,例如有的時候我們需要繪製一些圖形可能需要設定一個漂亮的背景,如果UI MM不給切圖的話,這個時候我們可能就會選擇漸層填充的方式。
Quartz 2D的漸層方式分為兩種:
a.線性漸層線:漸層色以直線方式從開始位置逐漸向結束位置漸層
b.放射狀漸層:以中心點為圓心從起始漸層色向四周輻射,直到終止漸層色。
要做漸層則必須先設定從開始位置到結束位置的漸層顏色,UI MM可定對於漸層色設定並不陌生,只需要在指定位置指定不同的顏色,剩下的事情交給系統處理就行。如,在起始位置,3/10位置,結束位置指定了三種顏色就形成了由三種顏色組成的漸層色。
另外,在iOS中繪製漸層還需要注意一點就是指定色彩空間,所謂色彩空間就是不同顏色在不同的維度上取值,最終組成一種顏色的過程。就拿RGB來說,如果將紅色,綠色,藍色看成是x、y、z軸座標系,那麼在三個座標上分別取0-255範圍內的不同值則可以組成各類顏色。當然,不同色彩空間的座標系也是不同的,也就是說顏色表示的方式是不同的,常用的色彩空間除了RGB還有CMYK(印刷業常用這種顏色模式)、Gray.
在使用Quartz 2D繪圖時我們的顏色除了使用常規的方法(如CGContextSetRGBFillColor(CGContextRef context, CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)方法)設定RGB和透明度之外,有時還會遇到顏色參數是一個數組情況。如使用色彩空間填充時用到的CGContextSetFillColor(CGContextRef context, const CGFloat *components)方法,這個時候components數組中具體是如何儲存顏色就要根據色彩空間而定,如果色彩空間使用RGB,則數組中的元素四個為一組,分別是red、green、blue、alpha;如果使用的是CMYK色彩空間,那麼數組中的元素五個為一組,分別是cyan、magenta、yellow、black、alpha。
下面,分別使用一下這兩種漸層的方式:
1.線性漸層
override func draw(_ rect: CGRect) { // 擷取上下文 let contextRef = UIGraphicsGetCurrentContext() // 使用RGB色彩空間 let colorSpace = CGColorSpaceCreateDeviceRGB() // 指定漸層色 /* colorSpace : 色彩空間 colorComponents : 顏色數組,由於指定了為RGB色彩空間,那麼四個數組元素就表示一個顏色(red\green\blue\alpha),如果有三個顏色,那麼這個數組中就要有 3 * 4 個元素。 locations : 顏色所在的位置 0 - 1,這個數組的個數不小於顏色的個數 count : 漸層的個數,等於locations的count */ let colorComponents : [CGFloat] = [248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0] let locations : [CGFloat] = [0,0.3,1.0] let gradient = CGGradient(colorSpace: colorSpace, colorComponents: colorComponents, locations: locations, count: 3) // 繪製線性色彩坡形 /* start : 起始座標 end : 結束座標 options :繪製方式,kCGGradientDrawsBeforeStartLocation 開始位置之前就進行繪製,到結束位置之後不再繪製, kCGGradientDrawsAfterEndLocation開始位置之前不進行繪製,到結束點之後繼續填充 */ contextRef?.drawLinearGradient(gradient!, start: CGPoint(x : 0, y : 20), end: CGPoint(x : 0, y : self.frame.size.height - 40), options: .drawsBeforeStartLocation) }
2.放射狀漸層
override func draw(_ rect: CGRect) { // 擷取上下文 let contextRef = UIGraphicsGetCurrentContext() // 使用RGB色彩空間 let colorSpace = CGColorSpaceCreateDeviceRGB() // 指定漸層色 /* colorSpace : 色彩空間 colorComponents : 顏色數組,由於指定了為RGB色彩空間,那麼四個數組元素就表示一個顏色(red\green\blue\alpha),如果有三個顏色,那麼這個數組中就要有 3 * 4 個元素。 locations : 顏色所在的位置 0 - 1,這個數組的個數不小於顏色的個數 count : 漸層的個數,等於locations的count */ let colorComponents : [CGFloat] = [248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0] let locations : [CGFloat] = [0,0.3,1.0] let gradient = CGGradient(colorSpace: colorSpace, colorComponents: colorComponents, locations: locations, count: 3) /*繪製放射狀漸層 context:圖形上下文 gradient:漸層色 startCenter:起始點位置 startRadius:起始半徑(通常為0,否則在此半徑範圍內容無任何填充) endCenter:終點位置(通常和起始點相同,否則會有位移) endRadius:終點半徑(也就是漸層的擴散長度) options:繪製方式,kCGGradientDrawsBeforeStartLocation 開始位置之前就進行繪製,但是到結束位置之後不再繪製, kCGGradientDrawsAfterEndLocation開始位置之前不進行繪製,但到結束點之後繼續填充 */ let center = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2) contextRef?.drawRadialGradient(gradient!, startCenter: center, startRadius: 0, endCenter: center, endRadius: 30, options: .drawsAfterEndLocation) }
3.漸層填充
上面我們只是繪製漸層到圖形上下文,實際開發中有時候我們還需要填充對應的漸層色,例如現在繪製了一個矩形,如何填充成漸層色呢?在此可以利用漸層裁切來完成(當然利用層CALayer更加方便),特別說明一下地區裁切並不僅僅適用於漸層填充,對於其他圖形繪製仍然適用,並且注意裁切只能限於矩形裁切。
override func draw(_ rect: CGRect) { // 擷取上下文 let contextRef = UIGraphicsGetCurrentContext() // 使用RGB色彩空間 let colorSpace = CGColorSpaceCreateDeviceRGB() //裁切還可以使用UIKit中對應的方法 UIRectClip(CGRect(x: 0, y: 20, width: self.frame.size.width, height: self.frame.size.height - 40)) // 指定漸層色 /* colorSpace : 色彩空間 colorComponents : 顏色數組,由於指定了為RGB色彩空間,那麼四個數組元素就表示一個顏色(red\green\blue\alpha),如果有三個顏色,那麼這個數組中就要有 3 * 4 個元素。 locations : 顏色所在的位置 0 - 1,這個數組的個數不小於顏色的個數 count : 漸層的個數,等於locations的count */ let colorComponents : [CGFloat] = [248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0] let locations : [CGFloat] = [0,0.3,1.0] let gradient = CGGradient(colorSpace: colorSpace, colorComponents: colorComponents, locations: locations, count: 3) // 繪製線性色彩坡形 /* start : 起始座標 end : 結束座標 options :繪製方式,kCGGradientDrawsBeforeStartLocation 開始位置之前就進行繪製,到結束位置之後不再繪製, kCGGradientDrawsAfterEndLocation開始位置之前不進行繪製,到結束點之後繼續填充 */ contextRef?.drawLinearGradient(gradient!, start: CGPoint(x : 0, y : 20), end: CGPoint(x : 0, y : self.frame.size.height - 40), options: .drawsBeforeStartLocation) }