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.
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.
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)
])
}
}
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))
iOS 17 and later
As of iOS 17, pie and donut charts are now included in Apple's Charts framework.
Simple pie chart example:
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:
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:
angularInset
to create spacing between sectors.annotation
to label the sectors.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.
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()
}
Yes it is possible in Apple's new swift UI by using charts, swiftUI and foundation libraries
© 2022 - 2024 — McMap. All rights reserved.
Circle()
&trim
to build one though. – Mezzosoprano