練習筆記48 : 利用 UIBezierPath 實現圓環進度條,甜甜圈圖表 & 圓餅圖

之前的作業用UIBezierPath跟CAShapeLayer互相搭配繪圖過

這次要練習做出三種圖表:圓環進度條/甜甜圈圖表 /圓餅圖

圓環進度條(circular progress ring)

圓環進度條的做法主要是做出一個完整的圓環circleLayer及一個進度條percentageLayer

circleLayer對應的circlePath是用 UIBezierPath(ovalIn: CGRect(x:, y: , width: , height: )) 畫出一個圓形

percentageLayer對應的percentagePath則是用UIBezierPath(arcCenter: 圓心座標, radius: 半徑, startAngle:圓弧開始角度, endAngle: 圓弧結束角度, clockwise: 是否順時針) 畫出曲線

接著將做好的兩個layer加到view上

並且可以依照需求加上文字label

import UIKit

//基本設定
let view = UIView(frame: CGRect(x: 0, y: 0, width: 2*(radius+lineWidth), height: 2*(radius+lineWidth)))
let aDegree = CGFloat.pi/180
let lineWidth = 10
let startDegree = 270
let radius = 50
let percentage :CGFloat = 60

//circleLayer
let circlePath = UIBezierPath(ovalIn: CGRect(x: lineWidth, y: lineWidth, width: radius*2, height: radius*2))
let circleLayer = CAShapeLayer()
circleLayer.path = circlePath.cgPath
circleLayer.strokeColor = UIColor.lightGray.cgColor
circleLayer.lineWidth = CGFloat(lineWidth)
circleLayer.fillColor = UIColor.clear.cgColor

//percentageLayer
let percentagePath = UIBezierPath(arcCenter: CGPoint(x: lineWidth+radius, y: lineWidth+radius), radius: CGFloat(radius), startAngle: CGFloat(startDegree)*aDegree, endAngle: (CGFloat(startDegree)+percentage/100*360)*aDegree, clockwise: true)
let percentageLayer = CAShapeLayer()
percentageLayer.path = percentagePath.cgPath
percentageLayer.strokeColor = UIColor.blue.cgColor
percentageLayer.lineWidth = CGFloat(lineWidth)
percentageLayer.fillColor = UIColor.clear.cgColor
percentageLayer.lineCap = .round

//label
let label = UILabel(frame: view.frame)
label.text = "\(percentage)%"
label.textAlignment = .center

//各個 layer/view 加入view
view.layer.addSublayer(circleLayer)
view.layer.addSublayer(percentageLayer)
view.addSubview(label)
view

甜甜圈圖表(donut chart)

製作甜甜圈圖表時先建立一個array儲存各個比例的值

再利用for迴圈產生各個比例對應的圖

percentageLayer的圖形可以透過設定lineWidth 將線條加寬製造出中空的甜甜圈狀 並用strokeColor更改顏色

最後建立一個產生label的function ㄧ起加入for迴圈裡

import UIKit

//基本設定
let aDegree = CGFloat.pi/180
let lineWidth = 50
let radius = 50
let view = UIView(frame: CGRect(x: 0, y: 0, width: (lineWidth+radius)*2, height: (lineWidth+radius)*2))
let center = CGPoint(x: Double(lineWidth+radius), y: Double(lineWidth+radius))
var percentages : [CGFloat] = [30,25,45]

var startDegree : CGFloat = 270
var endDegree : CGFloat = 0

//產生label
func createLabel(percentage:CGFloat,startDegree:CGFloat) -> UILabel{
//圓弧中心放label
let labelCenterDegree = startDegree + percentage/100*360/2
let labelPath = UIBezierPath(arcCenter: center, radius: CGFloat(radius), startAngle: labelCenterDegree*aDegree, endAngle: labelCenterDegree*aDegree, clockwise: true)
let label = UILabel(frame: CGRect(x: 0, y:0, width: 30, height: 15))
label.text = "\(percentage)%"
label.backgroundColor = UIColor.white
label.font = UIFont.systemFont(ofSize: 8)
label.textAlignment = .center
label.center = labelPath.currentPoint

return label
}

//用for迴圈產生各個比例的圖
for percentage in percentages {
endDegree = startDegree+percentage/100*360
//畫路徑
let percentagePath = UIBezierPath(arcCenter: center, radius: CGFloat(radius), startAngle: startDegree*aDegree, endAngle: endDegree*aDegree, clockwise: true)
//產生layer
let percentageLayer = CAShapeLayer()
percentageLayer.path = percentagePath.cgPath
percentageLayer.strokeColor = CGColor(red:CGFloat.random(in: 0...1), green: CGFloat.random(in: 0...1), blue: CGFloat.random(in: 0...1), alpha: 1)
percentageLayer.lineWidth = CGFloat(lineWidth)
percentageLayer.fillColor = UIColor.clear.cgColor

//將layer加到view上
view.layer.addSublayer(percentageLayer)
//將label加到view上
view.addSubview(createLabel(percentage: percentage, startDegree: startDegree))

//讓下一段弧線startDegree是上一段的endDegree
startDegree = endDegree
}
view

圓餅圖(pie chart)

圓餅圖的製作直接用剛剛甜甜圈圖表來改~

最主要的不同是用UIBezierPath產生percentagePath路徑時

要先將繪製的起點用.move(to:)指定為view的中心

再用.addArc畫出由中心點向外延伸的扇形

並設定percentageLayer的fillColor

最後再微調一下label的大小跟位置就完成了~~

import UIKit

//基本設定
let aDegree = CGFloat.pi/180
let lineWidth = 10
let radius = 50
let view = UIView(frame: CGRect(x: 0, y: 0, width: (lineWidth+radius)*2, height: (lineWidth+radius)*2))
let center = CGPoint(x: Double(lineWidth+radius), y: Double(lineWidth+radius))
var percentages : [CGFloat] = [30,25,45]

var startDegree : CGFloat = 270
var endDegree : CGFloat = 0

//產生label
func createLabel(percentage:CGFloat,startDegree:CGFloat) -> UILabel{
//圓弧中心放label
let labelCenterDegree = startDegree + percentage/100*360/2
let labelPath = UIBezierPath(arcCenter: view.center, radius: CGFloat(radius/2), startAngle: labelCenterDegree*aDegree, endAngle: labelCenterDegree*aDegree, clockwise: true)
let label = UILabel(frame: CGRect(x: 0, y:0, width: 25, height: 10))
label.text = "\(percentage)%"
label.backgroundColor = UIColor.white
label.font = UIFont.systemFont(ofSize: 7)
label.textAlignment = .center
label.center = labelPath.currentPoint

return label
}

//用for迴圈產生各個比例的圖
for percentage in percentages {
endDegree = startDegree+percentage/100*360
//畫路徑 產生扇形圖案
let percentagePath = UIBezierPath()
percentagePath.move(to: view.center)
percentagePath.addArc(withCenter: view.center, radius: CGFloat(radius), startAngle: startDegree*aDegree, endAngle: endDegree*aDegree, clockwise: true)
//產生layer
let percentageLayer = CAShapeLayer()
percentageLayer.path = percentagePath.cgPath
percentageLayer.fillColor = CGColor(red:CGFloat.random(in: 0...1), green: CGFloat.random(in: 0...1), blue: CGFloat.random(in: 0...1), alpha: 1)

//將layer加到view上
view.layer.addSublayer(percentageLayer)
//將label加到view上
view.addSubview(createLabel(percentage: percentage, startDegree: startDegree))

//讓下一段弧線startDegree是上一段的endDegree
startDegree = endDegree
}
view

之前雖然已經做過幾個UIBezierPath的作業

但是我還是對UIBezierPath有點恐懼😭

覺得算位置角度好難😭😭😭

不過總算把這個作業完成了🥹

--

--