Swift Charts w SwiftUI — kompletny przewodnik po wykresach w iOS 26
Kompletny przewodnik po Swift Charts w SwiftUI na iOS 26: markery, interaktywna selekcja, przewijanie, dostępność i praktyczny dashboard z gotowym kodem.
Swift Charts to natywny, deklaratywny framework Apple do budowy wykresów w SwiftUI. Dodajesz wizualizację danych tak samo jak zwykły View, opisując „co ma się pojawić" zamiast rysowania piksel po pikselu. W iOS 26 framework doczekał się głębszej integracji z Liquid Glass, zestawu nowych markerów oraz uproszczonego API do selekcji punktów i przewijania. Ten przewodnik prowadzi Cię od pierwszego LineMark po interaktywny dashboard produkcyjny, z kompletnymi, uruchamialnymi listingami.
Swift Charts jest dostępny od iOS 16, natomiast w iOS 26 znacznie rozszerzono interaktywność (chartXSelection, chartScrollableAxes, wieloosiowe zaznaczenia).
Framework operuje na markerach: LineMark, BarMark, PointMark, AreaMark, RuleMark i SectorMark. Możesz je swobodnie mieszać w jednym wykresie.
Selekcja punktów w iOS 26 działa przez binding, więc jedna linia kodu zamienia statyczny wykres w tooltip z adnotacją.
Dla dużych zbiorów danych używaj chartScrollableAxes z chartXVisibleDomain. SwiftUI renderuje wtedy tylko widoczny wycinek.
Dostępność (VoiceOver, Audio Graphs) uzyskujesz przez accessibilityLabel i accessibilityValue na każdym markerze. Nie zapominaj o niej w produkcji.
W iOS 26 wykresy dziedziczą materiały Liquid Glass, więc dobrze komponują się z resztą UI bez ręcznego stylowania tła.
Czym jest Swift Charts i kiedy warto go używać?
Swift Charts został ogłoszony na WWDC 2022 razem z iOS 16 i od początku pomyślany był jako natywny odpowiednik zewnętrznych bibliotek typu Charts (dawnej ios-charts) czy DGCharts. Piszę tutoriale w Swifcie od czasów Objective-C i szczerze, do dziś pamiętam, jak żmudne bywało rysowanie wykresów przez Core Graphics albo klejenie API z bibliotek open source. Swift Charts zamyka tę erę. Framework jest deklaratywny, w pełni zintegrowany z @Observable, respektuje Dynamic Type, obsługuje Dark Mode i od iOS 17 potrafi generować Audio Graphs dla VoiceOver.
Kiedy sięgnąć po Swift Charts? W praktyce zawsze, gdy Twoja aplikacja pokazuje więcej niż jedną liczbę: analytics dashboardy, HealthKit, monitor wydajności, wizualizacja SwiftData, panele finansowe, statystyki gier w SpriteKit. Jeżeli wymagasz wsparcia dla iOS 15 lub starszych, framework nie zadziała. Pozostaje wtedy DGCharts lub SwiftUI Canvas. Dla nowych projektów, w których minimum to iOS 17 lub 26, wybór jest jednoznaczny.
W iOS 26 dochodzi jeszcze jedno: Liquid Glass w SwiftUI automatycznie przenosi się na tło Chart, więc wykresy embeddowane w karty i sheety wyglądają spójnie bez ręcznego stylowania. To odróżnia je od bibliotek trzecich, które zwykle mają własną estetykę i wymagają nadpisywania kolorów.
Twój pierwszy wykres w SwiftUI
Najkrótszy działający wykres w Swift Charts to trzy linie kodu wewnątrz ForEach otoczonego widokiem Chart. Model musi być Identifiable lub mieć jawny id:. Swift Charts nie wymaga @Observable, ale idealnie się z nim komponuje, kiedy chcesz odświeżać dane w reakcji na akcje użytkownika. Poniżej minimalny, kompletny przykład wykresu liniowego z dziennymi krokami z HealthKit.
import SwiftUI
import Charts
struct DailySteps: Identifiable {
let id = UUID()
let day: Date
let steps: Int
}
struct StepsChart: View {
let data: [DailySteps]
var body: some View {
Chart(data) { entry in
LineMark(
x: .value("Dzień", entry.day, unit: .day),
y: .value("Kroki", entry.steps)
)
.interpolationMethod(.catmullRom)
}
.frame(height: 220)
.padding()
}
}
PlottableValue (x: .value(...)) opisuje nie tylko wartość, ale też etykietę osi. Dzięki temu Swift Charts sam generuje sensowne osie i legendę. Jeżeli chcesz płynniejszą linię, ustaw interpolationMethod na .catmullRom; do wykresów finansowych bardziej pasuje .stepStart. Cały widok ma wysokość 220 pt, ale szerokość dziedziczy po rodzicu, więc automatycznie wypełni NavigationStack, Sheet czy widżet.
Rodzaje marków: LineMark, BarMark, PointMark i inne
Każdy typ wykresu w Swift Charts to inny marker. Wszystkie można łączyć w jednym Chart, co pozwala np. nałożyć średnią kroczącą (LineMark) na słupki (BarMark) i dorzucić linię progu (RuleMark). Poniżej lista głównych markerów, których używam praktycznie codziennie.
LineMark: wykres liniowy, dobry dla trendów czasowych.
BarMark: słupki; wspiera stackowanie przez foregroundStyle(by:).
PointMark: scatter plot; z symbolSize daje bubble chart.
AreaMark: obszar pod krzywą; świetny w kombinacji z LineMark.
RuleMark: pionowa lub pozioma linia progu, cel, uśrednienie.
SectorMark: wycinki (donut, pie); wprowadzone w iOS 17.
RectangleMark: heatmapa i kalendarze aktywności.
Poniżej wykres kombinowany: słupki dziennych przychodów, linia średniej z siedmiu dni i pozioma reguła z celem miesięcznym. Zwróć uwagę, że foregroundStyle przyjmuje PlottableValue, co automatycznie generuje legendę. Nie musisz pisać własnej.
Wykres kołowy w Swift Charts to SectorMark z angle: równym PlottableValue. Ustawienie innerRadius: .ratio(0.6) tworzy donut, a angularInset dodaje separator między wycinkami (użyteczne przy transparencji). Cała dokumentacja markerów jest zebrana w oficjalnej dokumentacji Charts.
Jak dodać interaktywność do wykresu w iOS 26?
Do iOS 16 interaktywność wymagała DragGesture i ręcznego mapowania współrzędnych przez ChartProxy. Od iOS 17, a w iOS 26 domyślnie, wystarczy modyfikator chartXSelection(value:) z bindingiem. Użytkownik długim naciśnięciem wybiera punkt, a Ty otrzymujesz jego wartość osi X. Reszta to prosta adnotacja albo overlay.
chartXSelection zwraca wartość osi (tu: Date), a nie samego rekordu, więc trzeba go dopasować ręcznie. Ta drobna niespójność jest świadoma: framework nie chce Cię zmuszać do trzymania danych w jednym słowniku. W iOS 26 istnieje też wariant chartXSelection(range:) zwracający ClosedRange, idealny do zaznaczania zakresu i pokazywania podsumowań.
Do przewijania dużych zbiorów służy chartScrollableAxes(.horizontal) plus chartXVisibleDomain(length:). Widok pokazuje wtedy okno np. 30 dni, a użytkownik przewija w lewo i w prawo. Możesz to łączyć z chartScrollPosition, żeby zapamiętać ostatnią pozycję albo podpiąć się pod @Observable ViewModel i logować widoczny zakres do analityki.
Style, kolory, osie i legenda
Domyślne osie Swift Charts są sensowne, ale w produkcji prawie zawsze chcesz je dostroić: format daty, gęstość znaczników, orientację etykiet. Modyfikator chartXAxis przyjmuje builder z AxisMarks, co daje pełną kontrolę bez konieczności rysowania osi ręcznie.
.chartXAxis {
AxisMarks(values: .stride(by: .day, count: 7)) { value in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.day().month(.narrow))
}
}
.chartYAxis {
AxisMarks(position: .leading) { value in
AxisGridLine().foregroundStyle(.secondary.opacity(0.2))
AxisValueLabel {
if let intVal = value.as(Int.self) {
Text(intVal, format: .number.notation(.compactName))
}
}
}
}
.chartYScale(domain: 0...20_000)
Format .notation(.compactName) zamienia 20 000 na „20 tys." (bardzo przydatne w Live Activity albo widżetach, gdzie miejsca jest mało). chartYScale(domain:) zapina oś na sztywno; bez tego framework skaluje ją do min/max danych, co powoduje „skakanie" wykresu przy odświeżeniach.
Kolory pobieraj z Colorów semantycznych (.accentColor, .blue, .pink), a nie z hex-a. Dzięki temu wykresy respektują Dark Mode oraz increase contrast. Jeżeli mimo wszystko potrzebujesz palety kategorycznej, użyj chartForegroundStyleScale z jawnym słownikiem, żeby kolory były deterministyczne między odświeżeniami. Kwestię palet pod względem dostępności bardzo dobrze opisuje sekcja Charting Data w Human Interface Guidelines.
Animacje, dostępność i wydajność
Animacje w Swift Charts włączasz zwykłym withAnimation wokół zmiany danych. Framework animuje położenie każdego markera, co dla wykresów liniowych daje efekt „interpolacji" między dwoma zbiorami, a dla słupków spring-owe wysuwanie się. Ważne: animuj zmiany stanu, nie sam widok. Poniżej fragment ViewModelu w stylu nowego @Observable.
@Observable
final class RevenueViewModel {
var range: DateInterval = .init(start: .now.addingTimeInterval(-7*24*3600),
end: .now)
var samples: [DailySteps] = []
func reload(with new: [DailySteps]) {
withAnimation(.smooth(duration: 0.35)) {
samples = new
}
}
}
Dostępność to obszar, który przez lata cierpiał w bibliotekach trzecich (VoiceOver „widział" tam pusty obraz). Swift Charts renderuje każdy marker jako osobny element accessibility. Dodaj do markerów accessibilityLabel i accessibilityValue, a użytkownicy VoiceOver dostaną też Audio Graphs: dźwiękową reprezentację krzywej, przydatną dla osób niewidomych. Kod poniżej pokazuje kompletny wzorzec.
Wydajność: dla zbiorów do kilku tysięcy punktów Chart radzi sobie bez optymalizacji. Powyżej, użyj chartScrollableAxes, agreguj dane po dniu lub tygodniu przed przekazaniem do widoku, i unikaj przeliczania modelu w kompilowaniu widoku (użyj @Observable albo cache'a). Jeżeli danych masz naprawdę dużo (setki tysięcy), rozważ pre-renderowanie do Image przez ImageRenderer. Wtedy interaktywność tracisz, ale scroll pozostaje płynny. W moim ostatnim projekcie z ~800 tys. samplami taki hybrydowy render obniżył czas pierwszego malowania z 1,2 s do 90 ms.
Praktyczny dashboard z metrykami
Poniżej kompletny widok „karty metryki", element, którego używam praktycznie w każdej aplikacji analytics. Łączy w sobie tytuł, aktualną wartość, mały sparkline (LineMark bez osi) i podpis zmiany procentowej. Jest w pełni deklaratywny, waży kilkadziesiąt linii i integruje się z SwiftData bez dodatkowego mostka.
struct MetricCard: View {
let title: String
let value: Double
let deltaPercent: Double
let series: [DailySteps]
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(title)
.font(.caption)
.foregroundStyle(.secondary)
HStack(alignment: .firstTextBaseline) {
Text(value, format: .number.notation(.compactName))
.font(.title.monospacedDigit().weight(.semibold))
Spacer()
DeltaBadge(value: deltaPercent)
}
Chart(series) { p in
AreaMark(
x: .value("Dzień", p.day, unit: .day),
y: .value("Wartość", p.steps)
)
.foregroundStyle(.linearGradient(
colors: [.accentColor.opacity(0.35), .clear],
startPoint: .top, endPoint: .bottom
))
LineMark(
x: .value("Dzień", p.day, unit: .day),
y: .value("Wartość", p.steps)
)
.foregroundStyle(.accentColor)
.interpolationMethod(.monotone)
}
.chartXAxis(.hidden)
.chartYAxis(.hidden)
.frame(height: 60)
}
.padding(16)
.background(.regularMaterial, in: .rect(cornerRadius: 16))
}
}
private struct DeltaBadge: View {
let value: Double
var body: some View {
let up = value >= 0
Label {
Text(value, format: .percent.precision(.fractionLength(1)))
} icon: {
Image(systemName: up ? "arrow.up.right" : "arrow.down.right")
}
.font(.caption.bold())
.foregroundStyle(up ? .green : .red)
.padding(.horizontal, 6)
.padding(.vertical, 3)
.background(
(up ? Color.green : Color.red).opacity(0.15),
in: .capsule
)
}
}
Karta ma jasną hierarchię: liczba jest najbardziej widoczna, delta pełni rolę scanning-cue, a sparkline daje kontekst historyczny. W siatce LazyVGrid kilkanaście takich kart tworzy dashboard w stylu App Store Connect lub TestFlight. Dla porównania kart między okresami, dodaj chartYScale(domain: yDomain) ze wspólnym zakresem, żeby wielkość sparkline'ów była porównywalna między metrykami. Inaczej wzrokowo zakłamują się proporcje (sam wpadłem w tę pułapkę na jednej z pierwszych prezentacji klientowi).
Jeżeli chcesz, żeby dashboard zapisywał ustawienia użytkownika (widoczne kolumny, zakres dat), połącz go z Foundation Models. Podpowiedzi generowane on-device na podstawie widocznych metryk sprawiają, że karty przestają być statyczne, a stają się interaktywnym asystentem analitycznym. To spójna architektura: jeden model danych, dwa punkty prezentacji.
Podsumowanie
Swift Charts w iOS 26 jest już frameworkiem dojrzałym. Obsługuje wszystkie typy wykresów, których na co dzień potrzebujesz, i wspiera interaktywność, przewijanie oraz dostępność bez pisania własnych gestów. W praktyce oznacza to, że nawet skomplikowany dashboard analytics możesz zbudować w kilkadziesiąt linijek deklaratywnego kodu, mając za darmo Dark Mode, VoiceOver, Dynamic Type i Liquid Glass. Jeżeli dopiero zaczynasz, wybierz LineMark na dziennych krokach, a jak tylko poczujesz się pewnie, przełącz się na chartXSelection. Wtedy wykresy staną się częścią doświadczenia użytkownika, a nie tylko obrazkiem na ekranie.
Najczęściej zadawane pytania
Od jakiej wersji iOS dostępny jest Swift Charts?
Swift Charts wymaga iOS 16 lub nowszego, iPadOS 16, macOS 13, watchOS 9 i tvOS 16. Interaktywna selekcja przez chartXSelection działa dopiero od iOS 17, a przewijanie w widżetach dopiero od iOS 26.
Czy Swift Charts jest darmowy?
Tak, Swift Charts jest częścią SDK Apple i nie wymaga żadnej dodatkowej licencji ani zależności. Jedyny „koszt" to minimalna wersja systemu, którą musisz ustawić w projekcie Xcode.
Jak zrobić wykres kołowy w Swift Charts?
Użyj SectorMark(angle: .value(...), innerRadius: .ratio(0.6)). Ustawienie innerRadius tworzy donut; angularInset dodaje przerwy między wycinkami. SectorMark wymaga iOS 17 lub nowszego.
Jak animować zmianę danych w Swift Charts?
Owiń zmianę tablicy w withAnimation. Swift Charts sam animuje przejścia markerów między dwoma stanami. Dla płynnego efektu użyj .smooth(duration: 0.3). Nie animuj samego widoku, animuj model.
Czy Swift Charts obsługuje VoiceOver i Audio Graphs?
Tak. Każdy marker jest osobnym elementem accessibility. Dodaj accessibilityLabel i accessibilityValue, a użytkownicy VoiceOver otrzymają zarówno odczyt tekstowy, jak i dźwiękową reprezentację przez Audio Graphs (od iOS 17).
Kiedy Swift Charts nie jest dobrym wyborem?
Gdy potrzebujesz wsparcia poniżej iOS 16, wykresów 3D albo bardzo specyficznych typów (Sankey, sunburst, radar). W takich przypadkach lepiej sięgnąć po DGCharts lub napisać własne renderowanie na Canvas z Path.
Live Activities w iOS 26 dają aplikacji żywą kartę na ekranie blokady i w Dynamic Island. Przewodnik pokazuje ActivityKit, Push-to-Start, interaktywne App Intents i Liquid Glass z pełnym kodem Swift 6.2 dla Xcode 26.3.
Praktyczny przewodnik po TipKit w SwiftUI na iOS 26. Konfiguracja TipsCenter, reguły eligibility, popoverTip vs TipView, grupy wskazówek i testowanie z gotowymi przykładami kodu.
Praktyczny przewodnik po frameworku App Intents w iOS 26. Pokazujemy, jak budować AppIntent, AppShortcut, AppEntity oraz nowość WWDC25 — Interactive Snippets, czyli interaktywne widoki SwiftUI w Siri i Shortcuts bez otwierania aplikacji.