article

Tuesday, August 10, 2021

SwiftUI Pie Chart

SwiftUI Pie Chart
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()
    }
}

Related Post