Swift Charts (iOS 16) Pie / Donut Chart
Asked Answered
S

3

8

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.

Strade answered 16/9, 2022 at 9:35 Comment(3)
Not supported yet, you can use Circle() & trim to build one though.Mezzosoprano
Thanks @Timmy, I am moving from my custom library to the apple one and in the WWDC they are shown pie chart in the examples, but couldn't find any documentationStrade
I got reply from Apple Developer Technical Support that, Currently there is no support for pie charts in Swift Charts. I recommend filing an enhancement request asking for this functionalityStrade
O
12

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

Octogenarian answered 22/9, 2022 at 10:12 Comment(5)
I got reply from Apple Developer Technical Support that, Currently there is no support for pie charts in Swift Charts. I recommend filing an enhancement request asking for this functionalityStrade
Any suggestions for how to cut in half and still keep the complete chart output?Feer
Ash! Just change 360 to 180 👌Feer
Can you please provide some context on how is the donut clipping working?Armstrong
@Armstrong clipping means you set an area on the canvas that drawing will happen in; think of it like a stencil being held over the canvasOctogenarian
B
4

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()
}
Bantling answered 10/6, 2023 at 17:8 Comment(4)
Is there a way to move the annotations outside of the pie chart?Weiler
Possibly but in my testing only the overlay and centered annotation seems to work with pie charts at this time.Bantling
How to change the colors of the pie chart?Katerine
@Katerine Using .foregroundStyle, a specific color could be applied to each sector OR using .chartForegroundStyleScale's range could apply an array of colors that is applied to the sectors in array order.Bantling
R
-9

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

Rasberry answered 16/9, 2022 at 9:43 Comment(3)
Even though OP asks whether it's possible, he wants to know how. This answer is not helpful as it is.Maeganmaelstrom
thanks @Maeganmaelstrom for the clarification, in the WWDC video they are showing many more types of charts including pie but there is no code or sample or documentation :(Strade
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Caron

© 2022 - 2024 — McMap. All rights reserved.