article

Tuesday, August 10, 2021

SwiftUI Pie Chart

SwiftUI Pie Chart
ContentView.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//
//  ContentView.swift
//  swiftuidev
//
//  Created by Cairocoders
//
 
import SwiftUI
 
struct PieChartCell: Shape {
    let startAngle: Angle
    let endAngle: Angle
   func path(in rect: CGRect) -> Path {
        let center = CGPoint.init(x: (rect.origin.x + rect.width)/2, y: (rect.origin.y + rect.height)/2)
    let radii = min(center.x, center.y)
        let path = Path { p in
            p.addArc(center: center,
                     radius: radii,
                     startAngle: startAngle,
                     endAngle: endAngle,
                     clockwise: true)
            p.addLine(to: center)
        }
        return path
   }
}
 
struct PieChart: View {
    @State private var selectedCell: UUID = UUID()
     
    let dataModel: ChartDataModel
    let onTap: (ChartCellModel?) -> ()
    var body: some View {
            ZStack {
                ForEach(dataModel.chartCellModel) { dataSet in
                    PieChartCell(startAngle: self.dataModel.angle(for: dataSet.value), endAngle: self.dataModel.startingAngle)
                        .foregroundColor(dataSet.color)
                       .onTapGesture {
                         withAnimation {
                            if self.selectedCell == dataSet.id {
                                self.onTap(nil)
                                self.selectedCell = UUID()
                            } else {
                                self.selectedCell = dataSet.id
                                self.onTap(dataSet)
                            }
                        }
                    }.scaleEffect((self.selectedCell == dataSet.id) ? 1.05 : 1.0)
                }
            }
    }
}
 
 
struct ContentView: View {
    @State var selectedPie: String = ""
    @State var selectedDonut: String = ""
     
    var body: some View {
            ScrollView {
                VStack {
                    HStack(spacing: 20) {
                        PieChart(dataModel: ChartDataModel.init(dataModel: sample), onTap: {
                            dataModel in
                            if let dataModel = dataModel {
                                self.selectedPie = "Programming Language: \(dataModel.name)\nPercent: \(dataModel.value)"
                            } else {
                                self.selectedPie = ""
                            }
                        })
                            .frame(width: 150, height: 150, alignment: .center)
                            .padding()
                        Text(selectedPie)
                        .font(.footnote)
                        .multilineTextAlignment(.leading)
                        Spacer()
                         
                    }
                    Spacer()
                    HStack {
                        ForEach(sample) { dataSet in
                            VStack {
                                Circle().foregroundColor(dataSet.color)
                                Text(dataSet.name).font(.footnote)
                            }
                        }
                    }
                }
            }
    }
}
 
struct ChartCellModel: Identifiable {
    let id = UUID()
    let color: Color
    let value: CGFloat
    let name: String
}
 
final class ChartDataModel: ObservableObject {
    var chartCellModel: [ChartCellModel]
    var startingAngle = Angle(degrees: 0)
    private var lastBarEndAngle = Angle(degrees: 0)
     
         
    init(dataModel: [ChartCellModel]) {
        chartCellModel = dataModel
    }
     
    var totalValue: CGFloat {
        chartCellModel.reduce(CGFloat(0)) { (result, data) -> CGFloat in
            result + data.value
        }
    }
     
    func angle(for value: CGFloat) -> Angle {
        if startingAngle != lastBarEndAngle {
            startingAngle = lastBarEndAngle
        }
        lastBarEndAngle += Angle(degrees: Double(value / totalValue) * 360 )
        print(lastBarEndAngle.degrees)
        return lastBarEndAngle
    }
}
 
let sample = [ ChartCellModel(color: Color.red, value: 11, name: "Javascript"),
               ChartCellModel(color: Color.yellow, value: 10, name: "Swift"),
               ChartCellModel(color: Color.gray, value: 24, name: "Python"),
               ChartCellModel(color: Color.blue, value: 31, name: "Java"),
               ChartCellModel(color: Color.green, value: 12, name: "PHP")]
 
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Related Post