『iOS APP-11–2』UIBezierPath 進階練習

練習完百分比進度環後,想說再練習整合之前學過的。
花了滿多時間還有請教 Peter,最後終於生出來啦!

原先希望的效果是可以點選日期後,設定當天的預算,接著每花一筆錢輸入一筆資料,可以透過百分比環了解今天還剩多少預算。
不過發現目前學到的知識還不足以刻出來(笑

附上簡易的效果~

Date Picker

要加入日曆月曆的話,可以直接加入 Date Picker 的視窗。
右邊視窗的 Preferred Style 可以選擇 Date Picker 的樣式,因為要呈現完整日期,所以選擇 Inline 。

百分比進度環

為了讓百分比環會依照在畫面輸入的數字運作,這邊在 func 中設定了 spentMoney 跟 allMoney 兩項變數。

起始角度 270 度 (上方),終止角度為 270 度 + 360 * (100 - (spentMoney / allMoney * 100) / 100)。

我這邊卡超久,因為整個程式運作後,發現輸入數字百分比進度環也不會跟著跑,而是感覺圖層一層一層的疊上去。所以只好跑去請教 Peter ,才發現有指令可以把前一個圖層消掉!!!

所以這邊加了 view.layer.sublayers?.last?.removeFromSuperlayer(),可以在運作此 function 時,先消除前一個圖層,再畫百分比環。

func percentageCircle(spentMoney: Double, allMoney: Double) {

view.layer.sublayers?.last?.removeFromSuperlayer() // 消除前一個圖層用

let percentagePAth = UIBezierPath(arcCenter: circleView.center, radius: radius, startAngle: aDegree * startDegree, endAngle: aDegree * ( startDegree + 360 * (100 - (spentMoney / allMoney * 100))/100), clockwise: true)

let percentageLayer = CAShapeLayer()
percentageLayer.path = percentagePAth.cgPath
percentageLayer.lineWidth = lineWidth - 15
percentageLayer.fillColor = UIColor.clear.cgColor
percentageLayer.strokeColor = UIColor.cyan.cgColor
percentageLayer.lineCap = .round
view.layer.addSublayer(percentageLayer)

}

其餘就如上一篇一樣,宣告一些角度、線寬、半徑、起始角度的變數,利用 UIBezierPath 畫出線條,CAShapeLayer( ) 的 function 來繪製圓圈。

    let aDegree = CGFloat.pi/180
let lineWidth: CGFloat = 50
let radius: CGFloat = 150
let startDegree: CGFloat = 270

//繪製底層圓圈
func fullCircle() {
let fullCirclePath = UIBezierPath(arcCenter: circleView.center, radius: radius, startAngle: aDegree * startDegree, endAngle: aDegree * ( startDegree - 360), clockwise: false)
let fullCircleLayer = CAShapeLayer()
fullCircleLayer.path = fullCirclePath.cgPath
fullCircleLayer.lineWidth = lineWidth
fullCircleLayer.fillColor = UIColor.clear.cgColor
fullCircleLayer.strokeColor = UIColor.cyan.cgColor
fullCircleLayer.opacity = 0.25
view.layer.addSublayer(fullCircleLayer)
overrideUserInterfaceStyle = .dark
}

func outsideCircle() {
let outsideCirclePath = UIBezierPath(arcCenter: circleView.center, radius: radius + lineWidth / 2, startAngle: aDegree * startDegree, endAngle: aDegree * (startDegree + 360), clockwise: true)
let outsideCircleLayer = CAShapeLayer()
outsideCircleLayer.path = outsideCirclePath.cgPath
outsideCircleLayer.lineWidth = 3
outsideCircleLayer.fillColor = UIColor.clear.cgColor
outsideCircleLayer.strokeColor = UIColor.cyan.cgColor
view.layer.addSublayer(outsideCircleLayer)
}

func insideCircle() {
let insideCirclePath = UIBezierPath(arcCenter: circleView.center, radius: radius - lineWidth / 2, startAngle: aDegree * startDegree, endAngle: aDegree * (startDegree + 360), clockwise: true)
let insideCircleLayer = CAShapeLayer()
insideCircleLayer.path = insideCirclePath.cgPath
insideCircleLayer.lineWidth = 3
insideCircleLayer.fillColor = UIColor.clear.cgColor
insideCircleLayer.strokeColor = UIColor.cyan.cgColor
view.layer.addSublayer(insideCircleLayer)
}


//繪製全滿的百分比環
func hundredPercentageCircle() {
let percentagePAth = UIBezierPath(arcCenter: circleView.center, radius: radius, startAngle: aDegree * startDegree, endAngle: aDegree * ( startDegree + 360 ), clockwise: true)

let percentageLayer = CAShapeLayer()
percentageLayer.path = percentagePAth.cgPath
percentageLayer.lineWidth = lineWidth - 15
percentageLayer.fillColor = UIColor.clear.cgColor
percentageLayer.strokeColor = UIColor.cyan.cgColor
percentageLayer.lineCap = .round
view.layer.addSublayer(percentageLayer)
}

之後另外設置一個 func backgroundCircle( ) 放入上面繪製的圓圈。

func backgroungCircle() {
fullCircle()
outsideCircle()
insideCircle()
hundredPercentageCircle()
}

接著會用到 .viewDidLoad( ) 在畫面載入完成時,也將 backgroundCircle( )的同時顯示在畫面上。就會看到下圖啦!

 override func viewDidLoad() {
super.viewDidLoad()
backgroungCircle()
}

畫面與程式碼做連結

我在 View 中放入 UIView 、UILabel、UITextField ,做 IBOutlet 的連結。

這邊的好處是一但設定好連結與位置,『百分比進度環的圓心』與『百分比文字區的中心』會一直跟著 UIView 的中心走。

上圖中計算符號則做 IBAction 連結,負責處理按壓按鈕後的指令。
這邊宣告兩個變數 number 與 number02,將畫面中輸入的資訊轉換成數字。

接著加入百分比進度環 percentageCircle(spentMoney: number!, allMoney: number02!),將 number 、 number02 分別放入 spentMoney 與 allMoney中。 輸入值不能為空白,所以加上『!』。

而畫面中的百分比文字區,則使用 .text 將字串 String(format: "%.1f", 100 — (number! / number02!) * 100) + "%" 做輸出。

 @IBAction func input(_ sender: Any) {
let number = Double(moneyCost.text!)
let number02 = Double(totalMoney.text!)
percentageCircle(spentMoney: number!, allMoney: number02!)
percentageText.text = String(format: "%.1f", 100 - (number! / number02!) * 100) + "%"
}

最後顯示的畫面效果

--

--