shinto Joseph
shinto Joseph

Reputation: 1079

Swift Charts (iOS 16) Pie / Donut Chart

Is it possible to create a pie / donut chart using Apple's new SwiftUI Charts framework?

There are images of a pie chart shown in the WWDC videos. Any help or code sample on how to create the pie chart will be much appreciated.

Upvotes: 8

Views: 7766

Answers (3)

Marcy
Marcy

Reputation: 6009

iOS 17 and later

As of iOS 17, pie and donut charts are now included in Apple's Charts framework.


Simple pie chart example:

enter image description here

Add a SectorMark to create a pie chart.


    import Charts
    
    struct ContentView: View {
        var body: some View {
            Chart(data, id: \.id) { element in
                SectorMark(
                    angle: .value("Count", element.count)
                )
                .foregroundStyle(by: .value("Type", element.type))
            }
            .frame(width: 350, height: 350)
        }
    }

Simple doughnut chart:

enter image description here

Add an innerRadius to the SectorMark to create a donut.


    struct ContentView: View {
        var body: some View {
            Chart(data, id: \.id) { element in
                SectorMark(
                    angle: .value("Count", element.count),
                    innerRadius: .ratio(0.5)
                )
                .foregroundStyle(by: .value("Type", element.type))
            }
            .frame(width: 350, height: 350)
        }
    }

Pie chart with added features:

enter image description here

  • Add angularInset to create spacing between sectors.
  • Add annotation to label the sectors.
  • Add chartLegend to hide the chart's legend.

    struct ContentView: View {
        var body: some View {
            Chart(data, id: \.id) { element in
                SectorMark(
                    angle: .value("Count", element.count),
                    angularInset: 2.0
                )
                .foregroundStyle(by: .value("Type", element.type))
                .annotation(position: .overlay, alignment: .center) {
                    VStack {
                        Text(element.type)
                            .font(.caption)
                        Text("\(element.count, format: .number.precision(.fractionLength(0)))")
                            .font(.caption)
                    }
                }
            }
            .chartLegend(.hidden)
            .frame(width: 350, height: 350)
        }
    }

Adding a specific color to each sector:

A desired color can be specified for each pie sector using chartForegroundStyleScale(domain: range:). Setting the range to an array of colors will apply each color in order. If the array has too few colors then the colors will repeat. If the array has too many colors then the extra colors will be ignored.

pie chart


struct ChartView3: View {
    
    var body: some View {
        Chart(data, id: \.id) { element in
            SectorMark(
                angle: .value("Count", element.count)
            )
            .foregroundStyle(by: .value("Type", element.type))

        }
        .frame(width: 350, height: 350)
        .chartForegroundStyleScale(domain: .automatic, range: [.cyan, .indigo, .blue, .purple, .pink])
    }
}

Instead of using chartForegroundStyleScale, another option is to apply the colors individually using .foregroundStyle() on each SectorMark.


Example data used by all charts above:

let data = [
    ShapeModel(type: "Circle",    count: 12),
    ShapeModel(type: "Square",    count: 10),
    ShapeModel(type: "Triangle",  count: 21),
    ShapeModel(type: "Rectangle", count: 15),
    ShapeModel(type: "Hexagon",   count: 8)
]

struct ShapeModel: Identifiable {
    var type: String
    var count: Double
    var id = UUID()
}

Upvotes: 4

jrturton
jrturton

Reputation: 119242

Swift Charts does not (yet?) support polar geometry, which is what you need to build pie charts.

I would recommend using a Canvas and drawing the information yourself, something like this:

struct Pie: View {

    @State var slices: [(Double, Color)]

    var body: some View {
        Canvas { context, size in
            let total = slices.reduce(0) { $0 + $1.0 }
            context.translateBy(x: size.width * 0.5, y: size.height * 0.5)
            var pieContext = context
            pieContext.rotate(by: .degrees(-90))
            let radius = min(size.width, size.height) * 0.48
            var startAngle = Angle.zero
            for (value, color) in slices {
                let angle = Angle(degrees: 360 * (value / total))
                let endAngle = startAngle + angle
                let path = Path { p in
                    p.move(to: .zero)
                    p.addArc(center: .zero, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
                    p.closeSubpath()
                }
                pieContext.fill(path, with: .color(color))

                startAngle = endAngle
            }
        }
        .aspectRatio(1, contentMode: .fit)
    }
}

struct Pie_Previews: PreviewProvider {
    static var previews: some View {
        Pie(slices: [
            (2, .red),
            (3, .orange),
            (4, .yellow),
            (1, .green),
            (5, .blue),
            (4, .indigo),
            (2, .purple)
        ])
    }
}

enter image description here

To make a donut chart, clip the canvas as the first thing you do in the rendering closure:

let donut = Path { p in
    p.addEllipse(in: CGRect(origin: .zero, size: size))
    p.addEllipse(in: CGRect(x: size.width * 0.25, y: size.height * 0.25, width: size.width * 0.5, height: size.height * 0.5))
}
context.clip(to: donut, style: .init(eoFill: true))

enter image description here

Upvotes: 12

your_helper
your_helper

Reputation: 1

Yes it is possible in Apple's new swift UI by using charts, swiftUI and foundation libraries

Upvotes: -9

Related Questions