How to Build a Pie Chart in SwiftUI
Use SectorMark inside a Chart view (Swift Charts, iOS 17+) to render native pie and donut charts with automatic legends and color theming — no third-party libraries needed.
import Charts
import SwiftUI
struct QuickPieChart: View {
let slices = [
("Design", 40.0), ("Dev", 35.0), ("QA", 25.0)
]
var body: some View {
Chart(slices, id: \.0) { label, value in
SectorMark(angle: .value("Hours", value))
.foregroundStyle(by: .value("Phase", label))
}
.frame(height: 260)
}
}
Full implementation
The implementation below wraps SectorMark in a reusable view that accepts a typed data model. Setting innerRadius: .ratio(0.55) converts the pie into a donut — a common design preference — while angularInset adds spacing between slices for clarity. A center label is layered on top with a ZStack overlay so the total value stays visible inside the ring.
import Charts
import SwiftUI
// MARK: - Data model
struct BudgetSlice: Identifiable {
let id = UUID()
let category: String
let amount: Double
}
// MARK: - Chart view
struct PieChartView: View {
let slices: [BudgetSlice]
var title: String = "Budget"
private var total: Double {
slices.reduce(0) { $0 + $1.amount }
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(title)
.font(.headline)
.foregroundStyle(.primary)
ZStack {
Chart(slices) { slice in
SectorMark(
angle: .value("Amount", slice.amount),
innerRadius: .ratio(0.55), // donut hole
angularInset: 2 // gap between slices
)
.cornerRadius(4)
.foregroundStyle(by: .value("Category", slice.category))
.accessibilityLabel(slice.category)
.accessibilityValue("\(Int(slice.amount)) dollars, \(Int(slice.amount / total * 100)) percent")
}
.frame(height: 280)
.chartLegend(position: .bottom, alignment: .center, spacing: 12)
// Center label overlay
VStack(spacing: 2) {
Text("Total")
.font(.caption)
.foregroundStyle(.secondary)
Text("$\(Int(total))")
.font(.title2.bold())
.foregroundStyle(.primary)
}
}
}
.padding()
.background(.background)
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: .black.opacity(0.06), radius: 8, y: 4)
}
}
// MARK: - Preview
#Preview {
PieChartView(slices: [
BudgetSlice(category: "Housing", amount: 1800),
BudgetSlice(category: "Food", amount: 640),
BudgetSlice(category: "Transport", amount: 310),
BudgetSlice(category: "Savings", amount: 500),
BudgetSlice(category: "Other", amount: 250)
], title: "Monthly Budget")
.padding()
.background(Color(.systemGroupedBackground))
}
How it works
-
SectorMark(angle:) — The
angleparameter takes aPlottableValue<Double>. Swift Charts converts raw values into proportional arcs automatically, so you pass raw dollars (or any Double) rather than pre-computing percentages. -
innerRadius: .ratio(0.55) — Any positive ratio punches a hole in the center, transforming a pie into a donut. A ratio of
0gives a classic solid pie;0.55–0.65is the typical donut sweet spot. - .foregroundStyle(by: .value("Category", …)) — This single modifier wires the chart's built-in color scale and auto-generates the legend. No manual color arrays required.
-
ZStack overlay — Because the donut has an empty center, a
ZStackplaces aVStackof Text labels perfectly in the middle. TheChartframe and the overlay share the sameZStackso they size together automatically. -
Accessibility modifiers —
.accessibilityLabeland.accessibilityValueon eachSectorMarkgive VoiceOver users both the category name and a human-readable percentage, which Swift Charts does not synthesize automatically for sector marks.
Variants
Exploded slice on tap
Highlight a selected slice by increasing its outer radius and reducing the inner radius, giving the classic "explode" effect. Track selection with a @State variable.
struct ExplodingPieChart: View {
let slices: [BudgetSlice]
@State private var selected: BudgetSlice.ID?
var body: some View {
Chart(slices) { slice in
let isSelected = slice.id == selected
SectorMark(
angle: .value("Amount", slice.amount),
innerRadius: .ratio(isSelected ? 0.45 : 0.55),
outerRadius: .ratio(isSelected ? 1.05 : 1.0),
angularInset: 2
)
.cornerRadius(4)
.foregroundStyle(by: .value("Category", slice.category))
.opacity(selected == nil || isSelected ? 1 : 0.5)
}
.frame(height: 280)
.chartAngleSelection(value: $selected) // iOS 17+
}
}
Custom color palette
Override the automatic color assignment with .chartForegroundStyleScale(). Pass a dictionary mapping category strings to Color values — e.g. .chartForegroundStyleScale(["Housing": .indigo, "Food": .orange, …]). This works identically to other mark types and keeps your brand palette consistent across chart styles.
Common pitfalls
- iOS 17 minimum required.
SectorMarkwas introduced in iOS 17 — it does not exist on iOS 16. If your deployment target is iOS 16, you must gate the chart withif #available(iOS 17, *)and provide a fallback UI. - Zero-value slices crash the chart. A
SectorMarkwithangle: .value("", 0)causes a layout assertion. Always filter out zero-value entries before passing data to theChartview:slices.filter { $0.amount > 0 }. - Legend overflow on small screens.
.chartLegend(position: .bottom)can overflow its container when there are more than ~5 items with long labels. For dense datasets, prefer a custom legend usingLazyVGridand disable the built-in legend with.chartLegend(.hidden).
Prompt this with Claude Code
When using Soarias or Claude Code directly to implement this:
Implement a pie chart in SwiftUI for iOS 17+. Use Charts framework and SectorMark with innerRadius for a donut style. Make it accessible (VoiceOver labels and values per slice). Support tap-to-select with an exploded slice effect using chartAngleSelection. Add a #Preview with realistic budget sample data.
In Soarias's Build phase, paste this prompt into the Claude Code panel after your screen mockup is approved — it will scaffold the chart component, wire it to your SwiftData model, and drop a ready-to-compile file into your Xcode project.
Related
FAQ
Does this work on iOS 16?
No. SectorMark is an iOS 17+ API. On iOS 16 the Swift Charts framework exists but only supports bar, line, area, point, rule, and rectangle marks. If you must support iOS 16, gate with if #available(iOS 17, *) and render a simple bar chart or a third-party solution as the fallback.
How do I show percentage labels directly on each slice?
Swift Charts does not yet support text annotations inside slices natively. The recommended approach is to compute each slice's midpoint angle in degrees and use a Canvas or overlay GeometryReader to position Text views manually. For most apps the center-label donut pattern (shown above) is a simpler, equally clear alternative.
What is the UIKit equivalent?
UIKit has no built-in pie chart. Pre-Swift Charts, the standard options were Core Graphics arcs drawn in CAShapeLayer, or third-party libraries like Charts (danielgindi). With Swift Charts and SectorMark you get a fully native, animation-capable, accessible pie chart without any dependencies — a significant improvement over the UIKit era.
Last reviewed: 2026-05-11 by the Soarias team.