10 H10: Prijslijst
De klok is mooi, maar soms wil je gewoon een lijstje zien: uur voor uur, wat kost de stroom? In dit hoofdstuk bouw je een prijslijst die de uren groepeert per dag, het huidige uur markeert, en netjes omgaat met ontbrekende data.
10.1 Wat gaan we bouwen?
De PriceSidebarView: een scrollbare lijst van uurprijzen, gegroepeerd per dag. Het huidige uur is gemarkeerd. Als data ontbreekt, ziet de gebruiker een duidelijke melding.

10.2 Playground-voorbeeld
import SwiftUI
import PlaygroundSupport
struct FruitLijst: View {
let fruit = ["Appel", "Banaan", "Kers", "Druif", "Peer"]
var body: some View {
List(fruit, id: \.self) { naam in
HStack {
Circle()
.fill(.green)
.frame(width: 10, height: 10)
Text(naam)
}
}
.frame(width: 200, height: 200)
}
}
PlaygroundPage.current.setLiveView(FruitLijst())Een lijst van fruit. Elke rij heeft een groen bolletje en een naam. Zo werkt List in SwiftUI.
10.3 Concept uitgelegd
10.3.1 List en ForEach: lijsten bouwen
List is de standaard manier om een lijst te tonen in SwiftUI. Je geeft hem een array en een manier om elk item te tonen:
List(prijzen, id: \.id) { prijs in
Text("\(prijs.hour):00")
}ForEach werkt hetzelfde maar kan ook buiten een List gebruikt worden — in een VStack of ScrollView:
ScrollView {
LazyVStack {
ForEach(prijzen) { prijs in
Text("\(prijs.hour):00")
}
}
}LazyVStack is een slimme versie van VStack: hij maakt rijen pas aan als ze zichtbaar worden. Handig bij lange lijsten.
10.3.2 Groeperen per dag
Om prijzen te groeperen per dag gebruik je Dictionary(grouping:by:):
let gegroepeerd = Dictionary(grouping: prijzen) { prijs in
Calendar.current.startOfDay(for: prijs.date)
}
// gegroepeerd is nu [Date: [EnergyPrice]]Dit geeft een woordenboek met als sleutel de begintijd van de dag, en als waarde de prijzen van die dag.
10.3.3 Het huidige uur markeren
Je kunt het huidige uur markeren door te checken of de datum van een rij overeenkomt met het huidige uur:
var isHuidigUur: Bool {
Calendar.current.isDate(prijs.date, equalTo: Date(), toGranularity: .hour)
}10.4 Code schrijven
Stap 1: de prijsrij
Maak een aparte struct voor één rij in de lijst:
import SwiftUI
// Bron: EnergyClock/PriceSidebarView.swift – PriceRow
struct PrijsRij: View {
let prijs: EnergyPrice
let colorMapper: EnergyColorMapper
var isHuidigUur: Bool {
Calendar.current.isDate(prijs.date, equalTo: Date(), toGranularity: .hour)
}
var body: some View {
HStack {
// Tijd als tekst
Text(prijs.date, format: .dateTime.hour().minute())
.font(.system(.body, design: .monospaced))
.frame(width: 80, alignment: .leading)
Spacer()
// Gekleurde stip
Circle()
.fill(colorMapper.color(for: prijs))
.frame(width: 8, height: 8)
// Prijs of streepje
if let bedrag = prijs.price {
Text(bedrag, format: .number.precision(.fractionLength(2)))
.font(.system(.body, design: .monospaced))
.frame(width: 70, alignment: .trailing)
} else {
Text("–")
.foregroundStyle(.secondary)
.frame(width: 70, alignment: .trailing)
}
}
.padding(.horizontal)
.padding(.vertical, 8)
.background(isHuidigUur ? Color.accentColor.opacity(0.15) : Color.clear)
}
}Stap 2: de volledige lijstview
import SwiftUI
// Bron: EnergyClock/PriceSidebarView.swift
struct PrijsLijstView: View {
let energyPrices: [EnergyPrice]
let colorMapper: EnergyColorMapper
// Bereken groepen één keer in init, niet bij elke render
let prijzenPerDag: [(datum: Date, prijzen: [EnergyPrice])]
init(energyPrices: [EnergyPrice], colorMapper: EnergyColorMapper) {
self.energyPrices = energyPrices
self.colorMapper = colorMapper
let gesorteerd = energyPrices.sorted { $0.date < $1.date }
let kalender = Calendar.current
let gegroepeerd = Dictionary(grouping: gesorteerd) { prijs in
kalender.startOfDay(for: prijs.date)
}
self.prijzenPerDag = gegroepeerd
.sorted { $0.key < $1.key }
.map { (datum: $0.key, prijzen: $0.value.sorted { $0.date < $1.date }) }
}
var body: some View {
ScrollView {
LazyVStack(spacing: 0) {
ForEach(prijzenPerDag, id: \.datum) { groep in
// Dag-header
HStack {
Text(groep.datum, format: .dateTime.weekday(.wide).day().month(.wide))
.font(.headline)
Spacer()
}
.padding(.horizontal)
.padding(.vertical, 8)
Divider()
// Prijzen voor deze dag
ForEach(groep.prijzen) { prijs in
PrijsRij(prijs: prijs, colorMapper: colorMapper)
Divider()
.padding(.leading)
}
}
}
}
.background(Color(NSColor.controlBackgroundColor))
}
}Stap 3: lege toestand
Gebruik ContentUnavailableView als er geen data is:
var body: some View {
if energyPrices.isEmpty {
ContentUnavailableView(
"Geen prijzen beschikbaar",
systemImage: "bolt.slash",
description: Text("Ververs om nieuwe data op te halen")
)
} else {
ScrollView {
// ... de lijst
}
}
}List en LazyVStack doen op het eerste gezicht hetzelfde, maar er zijn belangrijke verschillen:
Listheeft ingebouwde ondersteuning voor swipe-to-delete, selectie en reorder. Het heeft ook zijn eigen achtergrond en scheidingslijnen.LazyVStackin eenScrollViewgeeft je volledige controle over de opmaak. Geen ingebouwde swipe-actions, maar ook geen ongewenste standaard stijlen.
Lazy in LazyVStack betekent dat rijen pas worden aangemaakt als ze zichtbaar worden in de ScrollView. Voor een lijst van 24 rijen maakt dit weinig verschil. Bij duizenden rijen — denk aan een transactiegeschiedenis — scheelt dit aanzienlijk in geheugengebruik en opstarttijd.
List gebruikt intern ook lazy loading, maar doet dit automatisch zonder dat je dat in de naam ziet.
10.5 Apple documentatie
Meer over List:
developer.apple.com/documentation/swiftui/list
Meer over LazyVStack:
developer.apple.com/documentation/swiftui/lazyvstack
Meer over ContentUnavailableView:
developer.apple.com/documentation/swiftui/contentunavailableview
Zoek in de List-documentatie naar “Grouping” — dat laat zien hoe je een List kunt indelen met secties en headers.
10.6 Samenvatting
| Begrip | Betekenis |
|---|---|
List |
Een ingebouwde SwiftUI-lijstview |
ForEach |
Herhaalt een view voor elk item in een array |
LazyVStack |
Verticale stapel die rijen pas aanmaakt als ze zichtbaar zijn |
ScrollView |
Maakt de inhoud scrollbaar |
Dictionary(grouping:by:) |
Groepeert een array in een woordenboek |
ContentUnavailableView |
Toont een nette melding als er niets te laten zien is |
.monospaced |
Vaste letterbreedte — handig voor getallen die netjes uitlijnen |
10.7 Opdracht
Voeg aan de PrijsRij een tweede stijl toe:
- Als het huidig uur is: toon de prijs vet en vergroot de gekleurde stip naar 12×12.
- Als de prijs ontbreekt (
nil): toon het streepje in een andere kleur en voeg de tekst “Geen data” toe naast het streepje. - Voeg een header toe bovenaan de lijst met de naam van de databron en de laatste updatetijd.