SwiftUI 畫統計圓餅圖
xcode 13.3.1, swift 5.5, iOS 15.4
2022-08-08
如何在SwiftUI中畫統計圓餅圖
首先先畫一個寬度為100的藍色的圓圈
import SwiftUI
struct CircleExample: View {
var body: some View {
ZStack {
Color.brown.ignoresSafeArea()
Circle()
.inset(by: 50)
.stroke(lineWidth: 100)
.foregroundColor(.blue)
}
}
}
然後可以使用trim(from:to)去設置有畫多少個圓,0至1代表一個完整個圓圈,若0到0.25就只會畫一個4分之1的圓圈,代碼如下
import SwiftUI
struct CircleExample: View {
var body: some View {
ZStack {
Color.gray.opacity(0.2).ignoresSafeArea()
Circle()
.inset(by: 50)
.trim(from: 0.0, to: 0.25)
.stroke(lineWidth: 100)
.foregroundColor(.blue)
}
}
}
4分之1的圓圈
由於trim(from:to)的from和to參數只能為正,而在右邊的水平線是為0度的關係,若我們要畫一個不是從0度開始的圓餅,就需要rotationEffect的方法來幫忙做轉動需要的角度,如比把1/4圓餅的開始角度10度或-10度,代碼如下:
從10度開始的1/4圓餅
import SwiftUI
struct CircleExample: View {
var body: some View {
ZStack {
Color.gray.opacity(0.2).ignoresSafeArea()
Circle()
.inset(by: 50)
.trim(from: 0.0, to: 0.25)
.stroke(lineWidth: 100)
.foregroundColor(.blue)
.rotationEffect(.degrees(10))
}
}
}
從10度開始的1/4圓餅
從-10度開始的1/4圓餅
import SwiftUI
struct CircleExample: View {
var body: some View {
ZStack {
Color.gray.opacity(0.2).ignoresSafeArea()
Circle()
.inset(by: 50)
.trim(from: 0.0, to: 0.25)
.stroke(lineWidth: 100)
.foregroundColor(.blue)
.rotationEffect(.degrees(-10))
}
}
}
從-10度開始的1/4圓餅
知道上面的一些方法後,我們可以很容易在SwiftUI去實現一個統計的圓餅圖。假設我們想畫一個學校的男女學生和老師的統計圓餅,老師佔全校人數是4/8=0.5(藍色部分),男同學佔了3/8=0.375(黃色部分),女同學佔了1/8=0.125(綠色部分)。
實作如下:
import SwiftUI
struct CircleExample: View {
var body: some View {
ZStack {
Color.gray.opacity(0.2).ignoresSafeArea()
Circle()
.inset(by: 50)
.trim(from: 0.0, to: 0.5)
.stroke(lineWidth: 100)
.foregroundColor(.blue)
Circle()
.inset(by: 50)
.trim(from: 0.5, to: (0.5+0.375))
.stroke(lineWidth: 100)
.foregroundColor(.yellow)
Circle()
.inset(by: 50)
.trim(from: (0.5+0.375), to: 1)
.stroke(lineWidth: 100)
.foregroundColor(.green)
}
}
}
學校的男女學生和老師的統計圓餅
若每次數據改動或增減,都要自己去計算每個from和to的數值就很痛苦,以下代碼改動能輸入原始數據和每種數據的顏色,就可以畫出圓餅圖。
實作如下:
import SwiftUI
struct CircleExample: View {
var body: some View {
let nums : [Double] = [10.0, 30.0, 10.0]
let colors : [Color] = [.blue, .yellow, .green]
let degress = toDegree(nums)
return ZStack {
Color.gray.opacity(0.2).ignoresSafeArea()
ForEach(degress.indices, id:\.self) { i in
Circle()
.inset(by: 50)
.trim(from:degress[i].0, to: degress[i].1)
.stroke(lineWidth: 100)
.foregroundColor(colors[i])
}
}
}
func toDegree(_ nums : [Double]) -> [(Double, Double)] {
var sum = 0.0
var acculate : [Double] = []
var result : [(Double, Double)] = []
for i in nums {
acculate.append(sum)
sum += Double(i)
}
for i in 0..<nums.count {
result.append((acculate[i]/sum, (acculate[i]+nums[i])/sum))
}
return result
}
}