來畫個精美的百分比圓餅圖吧
Swift精進第003天-運用UIBezierPath及function編寫百分比圓餅圖
作業參考:
彼得潘的文章有細部說明UIBezierPath的用法及圓餅圖的起始角度等基本觀念,這邊就不贅述了~
這篇作業的學習目標還額外新增了幾個東西:
- 使用function來定義繪圖參數
- 使用到極座標
- 不要平的進度條,末端改為圓弧狀的
Function部分定義如下:
//可自訂的「圓環寬度」、「色彩」、「百分比」func circleRatio(width: CGFloat, colorRed: CGFloat, colorGreen: CGFloat, colorBlue: CGFloat, percentageOrg: CGFloat)
{}
極座標的部分找來找去都沒有,所以就直接在直角座標上寫入轉換極座標的公式吧~
一般直角座標是:P ( x , y )
也就是從原點向右位移x,再向上位移y
(不過在iOS當中的y是上下相反,所以是向下位移y)
而極座標是:P ( r , θ )
也就是從原點距離為r,且與x軸夾角為θ的地方
細節可以參考維基百科
圓形末端的進度條樣式剛好可以套用極座標的用法,想辦法在進度條末端的中心點上畫上一個一樣寬的圓形即可,雖然講起來很簡單,不過中間卻還是因為座標的問題想了一陣子才好不容易搞定
程式碼如下,使用Playground
var view = UIView()//建立一個畫圓型比例的Function
func circleRatio(width: CGFloat, colorRed: CGFloat, colorGreen: CGFloat, colorBlue: CGFloat, percentageOrg: CGFloat)
{
//設定一些參數
let aDegree = CGFloat.pi/180
//一般習慣從12點方向畫圓,所以從iOS座標的270度開始(iOS座標與卡式座標上下顛倒)
let startpointDegree: CGFloat = 270
let circlelineWidth: CGFloat = width
let radius: CGFloat = 50
let percentage = percentageOrg - (width / 2 / 2 / radius * 100 * 0.2)
let percentageDegree = aDegree * (startpointDegree + 360 * percentage / 100)//畫出百分比圖中心的圓
let innercirclePath = UIBezierPath(ovalIn: CGRect(x: circlelineWidth, y: circlelineWidth, width: radius*2, height: radius*2))
//建立圖層
let circleLayer = CAShapeLayer()
//將繪圖路徑設至圖層路徑上
circleLayer.path = innercirclePath.cgPath
//使用輸入的資料設定百分比線條,及線條色,內圓色
circleLayer.strokeColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1).cgColor
circleLayer.lineWidth = circlelineWidth
circleLayer.fillColor = UIColor.clear.cgColor//畫百分比外框
let circleCenter = CGPoint(x: circlelineWidth + radius, y: circlelineWidth + radius)
let percentagePath = UIBezierPath(arcCenter: circleCenter, radius: radius, startAngle: aDegree * startpointDegree, endAngle: percentageDegree, clockwise: true)
let percentageLayer = CAShapeLayer()
//將路徑放入圖層,設定外框及顏色
percentageLayer.path = percentagePath.cgPath
percentageLayer.strokeColor = UIColor(red: colorRed, green: colorGreen, blue: colorBlue, alpha: 1).cgColor
percentageLayer.lineWidth = circlelineWidth
percentageLayer.fillColor = UIColor.clear.cgColor//開始繪製末端的圓形
let endpointPath = UIBezierPath(ovalIn: CGRect(x: radius + circlelineWidth + cos(percentageDegree) * radius - width/2, y: radius + circlelineWidth + sin(percentageDegree) * radius - width/2, width: width, height: width))
let endpointLayer = CAShapeLayer()
endpointLayer.path = endpointPath.cgPath
//因為長外框的話會造成畫出來的球比實際的還大,所以此處把外框設為沒有顏色
endpointLayer.strokeColor = UIColor.clear.cgColor
endpointLayer.fillColor = UIColor(red: colorRed, green: colorGreen, blue: colorBlue, alpha: 1).cgColor//加入標籤
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 2*(radius+circlelineWidth), height: 2*(radius+circlelineWidth)))
label.textAlignment = .center
label.text = "\(percentageOrg)%"
view = UIView(frame: label.frame)//加入圖層及subview
view.layer.addSublayer(circleLayer)
view.layer.addSublayer(percentageLayer)
view.layer.addSublayer(endpointLayer)
view.addSubview(label)
}
補充說明一下有點容易忘掉的地方:
繪圖流程:UIBezierPath → CAShapeLayer → UIView
CAShapeLayer進入UIView是使用.layer.addSublayer
但是把UILabel(屬於UIView)放進UIView時要使用.addSubview
因為2者本來就是不同東西,自然是使用不同方式
執行結果:
circleRatio(width: 35, colorRed: 0.8, colorGreen: 0.4, colorBlue: 0.4, percentageOrg: 70)
view
circleRatio(width: 15, colorRed: 0.5, colorGreen: 0.9, colorBlue: 0.7, percentageOrg: 20)
view
circleRatio(width: 25, colorRed: 0.3, colorGreen: 0.6, colorBlue: 0.8, percentageOrg: 50)
view