Dynamische Infografiken
Diese View zeigt eine dynamische Infografik, die in Echtzeit auf Benutzereingaben oder externe Datenquellen reagiert. Durch animierte Grafikelemente werden Veränderungen optisch hervorgehoben und der Fortschritt anschaulich visualisiert.
🔍 Zweck
- Darstellung des Fitnessfortschritts (z.B. Schritte, Kalorien, Trainingsziele)
- Visualisierung von Finanzdaten (z.B. Ausgaben, Budgetplanung)
- Überwachung von Gesundheitsparametern (z.B. Herzfrequenz, Schlafdauer)
- Fortschrittsanzeige bei Lern- oder Sprach-Apps
- Live-Datenvisualisierung für IoT-Geräte oder Sensoren
📄 Codebeispiel
import SwiftUI
import Charts
// Model for representing progress data points
struct ProgressData: Identifiable, Equatable {
let id = UUID()
let date: Date
let value: Double
}
// Sample data generator for preview and simulation
extension Array where Element == ProgressData {
static func sampleData(days: Int = 14, range: ClosedRange<Double> = 20...100) -> [ProgressData] {
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())
return (0..<days).map { offset in
ProgressData(
date: calendar.date(byAdding: .day, value: -offset, to: today)!,
value: Double.random(in: range)
)
}.reversed()
}
}
struct DynamicInfographicView: View {
// Simulated data source; in a real app this could be bound to external data.
@State private var progressData: [ProgressData] = .sampleData()
// User input for dynamic updates
@State private var newValue: Double = 50
// Animation for chart changes
@Namespace private var animation
var body: some View {
VStack(spacing: 24) {
Text("Weekly Fitness Progress")
.font(.title2.bold())
.accessibilityAddTraits(.isHeader)
// Animated line chart of user's progress
Chart(progressData) { dataPoint in
LineMark(
x: .value("Date", dataPoint.date),
y: .value("Value", dataPoint.value)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(Color.accentColor.gradient)
.lineStyle(StrokeStyle(lineWidth: 4))
PointMark(
x: .value("Date", dataPoint.date),
y: .value("Value", dataPoint.value)
)
.foregroundStyle(Color.blue)
.symbolSize(50)
}
.chartYAxis(.visible)
.frame(height: 220)
.animation(.easeInOut(duration: 0.6), value: progressData)
HStack {
VStack(alignment: .leading) {
Text("Today's Value")
.font(.subheadline)
.foregroundStyle(.secondary)
Text("\(Int(progressData.last?.value ?? 0)) Points")
.font(.title3.bold())
.foregroundStyle(Color.accentColor)
.accessibilityLabel("Today's value is \(Int(progressData.last?.value ?? 0)) points")
}
Spacer()
// Circular progress bar with smooth animation
ZStack {
Circle()
.stroke(lineWidth: 11)
.opacity(0.12)
.foregroundColor(.accentColor)
Circle()
.trim(from: 0, to: CGFloat(min((progressData.last?.value ?? 0) / 100, 1)))
.stroke(
AngularGradient(gradient: Gradient(colors: [.accentColor, .green]), center: .center),
style: StrokeStyle(lineWidth: 11, lineCap: .round, lineJoin: .round)
)
.rotationEffect(.degrees(-90))
.animation(.spring(response: 0.5), value: progressData.last?.value)
Text("\(Int((progressData.last?.value ?? 0)))%")
.font(.headline.monospacedDigit())
.bold()
.foregroundColor(.primary)
}
.frame(width: 70, height: 70)
}
.padding(.horizontal)
Divider().padding(.vertical, 8)
// User input slider to simulate real-time updates
VStack(alignment: .leading) {
Text("Update Today's Progress")
.font(.footnote).bold()
Slider(value: $newValue, in: 0...100, step: 1) {
Text("Progress Value")
}
Button(action: addNewValue) {
Label("Set Value", systemImage: "waveform.path.ecg")
.fontWeight(.medium)
.padding(.vertical, 7)
.padding(.horizontal, 22)
.background(Capsule().foregroundColor(Color.accentColor.opacity(0.15)))
}
}
Spacer(minLength: 12)
}
.padding()
// Accessibility improvements
.accessibilityElement(children: .contain)
// Optional refresh button for simulated "external" update
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: randomizeToday) {
Image(systemName: "arrow.triangle.2.circlepath")
.imageScale(.large)
.accessibilityLabel("Randomize today's value")
}
}
}
}
// Update today's value in the progress data array with user input.
private func addNewValue() {
guard !progressData.isEmpty else { return }
withAnimation {
progressData[progressData.count - 1] = ProgressData(date: progressData.last!.date, value: newValue)
}
}
// Simulate an external data update (randomizes today's value).
private func randomizeToday() {
guard !progressData.isEmpty else { return }
withAnimation {
let randomValue = Double.random(in: 10...100)
progressData[progressData.count - 1] = ProgressData(date: progressData.last!.date, value: randomValue)
newValue = randomValue
}
}
}
#Preview {
DynamicInfographicView()
}