ContentView.swift
// // 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() } }