UIBezierPath是對Core Graphics框架的一種上層封裝,目的是讓繪圖需求可以被更方便的使用。
那你有沒有發現被UIBezierPath封裝後與之前有什麼改變?
答:有三個變化。
1.遮蔽繁雜重複的內容
2.功能閹割
3.座標系順時針方向反轉
證明1:遮蔽繁雜重複的內容
相比Core Graphics框架,UIBezierPath幫我們做了一些繁瑣的事件。比如有這樣一個場景:需要畫一個圓,但是它的每個1/4弧線的strokpath顏色是不同的。對於這樣的需求。
有個錯誤的做法是:
1.拿到上下文
2.設定第一個1/4戶的strokpath顏色,用上下文繪製第一個1/4弧
3.設定第一個2/4戶的strokpath顏色,用上下文繪製第一個2/4弧
4.設定第一個3/4戶的strokpath顏色,用上下文繪製第一個3/4弧
5.設定第一個4/4戶的strokpath顏色,用上下文繪製第一個4/4弧
最後的結果會發現,這四段弧的顏色是最後一個4/4弧的strokpath的顏色
原因是:對於一個上下文來說,strokPathColor屬性只有一個,雖然設定了4次,但總是後面的覆蓋前面的。
一種解決方法是:
在第2步之前,先迴圈4次操作
let content = UIGraphicsGetCurrentContext() content?.setStrokeColor(UIColor.blue.cgColor) content?.saveGState()
然後在每一步繪製前,恢復上下文棧中的儲存到當前上下文
content?.restoreGState()
另一種解決方法是:
直接建立4個UIBezierPath,用貝塞爾曲線繪製著4段弧。
這樣就很直觀的看出,每個UIBezierPath的上下文都是獨立的。內部幫我們自己做了上下文的存棧和出棧。
證明2:功能閹割
雖然有了UIBezierPath的封裝我們用起來方便了,但是相應的代價是所提供的功能被閹割了。有些強大的功能UIBezierPath沒有提供實現,比如:現在要畫一個圓形的漸變球,就只能使用Core Graphics框架。
程式碼如下
//利用上下文繪製漸變色(圓形) let context = UIGraphicsGetCurrentContext() //顏色空間 let colorSpace = CGColorSpaceCreateDeviceRGB() let startColor = UIColor.black let endColor = UIColor.red //顏色陣列 let colors = [startColor.cgColor,endColor.cgColor] //顏色所處位置 let locations:[CGFloat] = [0,1] let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as! CFArray, locations: locations) let center = CGPoint(x: rect.size.width*0.5, y: rect.size.height*0.5) let radius = rect.size.height*0.3 context?.drawRadialGradient(gradient!, startCenter: center, startRadius: radius*0.2, endCenter: center, endRadius: radius, options: CGGradientDrawingOptions.drawsBeforeStartLocation)
證明3:座標系順時針方向反轉
你知道嗎, CoreGraphics繪圖系統和Bezier貝塞爾曲線座標系的順時針方向是相反的!
我記得上學時老師講的座標系是這樣的:
X軸指向右側,Y軸指向上面。對應的弧度如圖上標的那樣。順時針也是鐘錶錶針轉動的方向。這就是最早接觸的座標系,熟悉的單純模樣。
在工作時,當我們往螢幕上佈局UI時,用到的座標系是下面這樣:
對於UI控制元件來講的座標系模式,X軸方向向右,Y軸方向向下。
請注意弧度值也相應的轉了方向,它是沿著X,Y指向的方向開始逐漸增加的。
順時針還是熟悉的鐘表表針轉動方向。
關鍵程式碼如下:
let content = UIGraphicsGetCurrentContext() var endAngl = _progressValue*CGFloat(M_PI*2) var clockState = (_direction == .onTime) //圓 var des: String = "" des = clockState ? "UIGraphics上下文繪製、順時針" : "UIGraphics上下文繪製、逆時針” content?.move(to: CGPoint(x: width-arcRadius, y: height*0.5)) let bez = UIBezierPath(arcCenter: arcCenter, radius: arcRadius, startAngle: 0, endAngle: endAngl, clockwise: clockState) content?.addPath(bez.cgPath) NSString(string: des).draw(in: CGRect(x: 2, y: 2, width: width*0.4, height: height*0.5), withAttributes: atts) log = String(format: "繪製弧度: %.4f Pi", endAngl/3.14) content?.strokePath()
實際效果圖如下:
UIBezierPath順時針模式下,從0到2PI的效果
UIBezierPath逆時針模式下,從0到2PI的效果
然後突出的CoreGraphics表示不服,我就要與眾不同。如下圖:
說出來你可能不信,你會發現順時針方向往上了。這明明是逆時針方向啊!WTF?
來看下程式碼和實現效果吧。
let content = UIGraphicsGetCurrentContext() var endAngl = _progressValue*CGFloat(M_PI*2) var clockState = (_direction == .onTime) //圓 var des: String = "" des = clockState ? "UIGraphics上下文繪製、順時針" : "UIGraphics上下文繪製、逆時針” content?.move(to: CGPoint(x: width-arcRadius, y: height*0.5)) content?.addArc(center: arcCenter, radius: arcRadius, startAngle: 0, endAngle: endAngl, clockwise: clockState) NSString(string: des).draw(in: CGRect(x: 2, y: 2, width: width*0.4, height: height*0.5), withAttributes: atts) log = String(format: "繪製弧度: %.4f Pi", endAngl/3.14) content?.strokePath()
實際效果如下:
CoreGraphics順時針模式下,從0到2PI的效果
CoreGraphics逆時針模式下,從0到2PI的效果
CoreGraphics和Bezier貝塞爾曲線都是平時開發中的利器,認真品味一下兩者的區別,會讓我們對它們有更深的認識。
有講的不對的地方歡迎指正。
Demo地址:https://github.com/zhfei/CoordinateSystem