LazyVStack: wat zijn verticale en horizontale luie stacks in SwiftUI

Auteur: IT Sectr Gepubliceerd: 2026-02-24 Leestijd: 10 min

Ontdek wat LazyVStack en LazyHStack zijn in SwiftUI — luie stacks voor efficiënte weergave van scrollbare lijsten, rasters en carrousels op iOS, macOS, watchOS en tvOS. In tegenstelling tot gewone VStack en HStack, maken luie stacks elementen pas aan wanneer ze in het zichtbare gebied verschijnen, wat het geheugengebruik bij grote datasets aanzienlijk vermindert. De architectuur van luie stacks is gebaseerd op het Layout-protocol en geïntegreerd met identificatie via ForEach en ScrollView.

Belangrijkste punten

  • Lazy loading — LazyVStack en LazyHStack maken onderliggende weergaven alleen aan wanneer ze het zichtbare gebied van ScrollView binnenkomen, wat geheugen bespaart en FPS verbetert tijdens het scrollen.
  • Identificatie via id — voor correcte werking met luie containers moet elk element in ForEach een unieke identificatie hebben (id: \.self of Identifiable-protocol).
  • pinnedViews — parameter maakt het mogelijk om sectiekopteksten (sectionHeaders, sectionFooters) vast te zetten aan de boven- of onderkant van het scherm tijdens het scrollen, vergelijkbaar met UITableView section headers.
  • spacing en alignment — beide stacks accepteren aangepaste tussenruimte en uitlijning (leading, center, trailing voor VStack; top, center, bottom voor HStack).
  • Nesting met GridItem — LazyVGrid en LazyHGrid zijn gebouwd op hetzelfde principe van lazy loading, maar met ondersteuning voor kolommen en rijen via GridItem.

Wat zijn LazyVStack en LazyHStack?

LazyVStack en LazyHStack — lay-outcontainers in SwiftUI die onderliggende weergaven alleen maken en tonen wanneer nodig, wanneer ze zichtbaar worden in het scrollbare gebied. LazyVStack plaatst elementen verticaal (van boven naar beneden), en LazyHStack — horizontaal (van links naar rechts).

Beide stacks zijn geïntroduceerd door Apple in SwiftUI 2.0 (iOS 14, macOS 11, watchOS 7, tvOS 14) samen met LazyVGrid en LazyHGrid. Voordat luie stacks verschenen, waren ontwikkelaars gedwongen om UITableView en UICollectionView via UIViewRepresentable te gebruiken voor efficiënt werken met grote lijsten. LazyVStack elimineerde deze noodzaak door een native SwiftUI-interface met automatisch lazy loading te bieden.

Volgens Apple WWDC Session 10031 (2020) gebruiken luie stacks het deferred view creation-mechanisme: SwiftUI slaat brongegevens op (bijv. een array van modellen) en maakt weergave-instanties vlak voordat ze op het scherm worden gerenderd. Tijdens het scrollen hergebruiken stacks reeds gemaakte weergaven, waardoor nieuwe allocaties worden vermeden — dit vermindert de belasting van de geheugenallocator en de garbage collector van Swift.

Om met luie stacks te werken, moet je ze in een ScrollView plaatsen — zonder scrollen worden elementen die buiten de schermgrenzen vallen gewoon afgeknipt, niet lui aangemaakt.

Hoe lazy loading werkt

Het lazy loading-mechanisme in LazyVStack is gebaseerd op geometrie: SwiftUI volgt de positie van elke onderliggende weergave ten opzichte van de ScrollView-container. Wanneer een element de grens van het zichtbare gebied overschrijdt (met een kleine buffer van enkele punten), roept het systeem de initialisator aan en rendert de inhoud. Wanneer het element het scherm verlaat, vernietigt SwiftUI de weergave, maar behoudt de status via @State indien gemarkeerd als behouden.

Deze benadering verschilt van VStack, waar alle onderliggende weergaven onmiddellijk worden gemaakt bij initialisatie van de container, ongeacht hun zichtbaarheid. Voor een lijst van 10.000 elementen maakt VStack 10.000 weergave-instanties in het geheugen, terwijl LazyVStack alleen degene maakt die op het scherm passen (meestal 8–15).

Afmetingen en uitlijning

LazyVStack accepteert drie configuratieparameters: alignment (HorizontalAlignment — leading, center, trailing), spacing (CGFloat — tussenruimte tussen elementen) en pinnedViews (PinnedScrollableViews — vastzetten van sectiekopteksten). LazyHStack gebruikt dezelfde parameters, maar alignment accepteert VerticalAlignment (top, center, bottom).

Verschillen tussen LazyVStack en VStack: prestaties en geheugen

Het belangrijkste verschil tussen LazyVStack en VStack — de strategie voor het maken van onderliggende elementen. VStack (eager stack) berekent de grootte en positie van alle onderliggende weergaven op het moment van renderen, wat het ongeschikt maakt voor grote dynamische lijsten. LazyVStack (luie stack) stelt het maken uit tot het moment dat het element zichtbaar wordt.

Laten we het gedrag vergelijken met een lijst van 1000 tekstregels. VStack laadt alle 1000 regels onmiddellijk in het geheugen, roept de initialisator van elke regel aan en wijst er geheugen aan toe. Dit leidt tot prestatievermindering op zwakkere apparaten (iPhone SE, iPad mini) en langere opstarttijd van het scherm. LazyVStack laadt alleen de zichtbare 10–12 regels en maakt de rest tijdens het scrollen.

Een praktische test (met Xcode Instruments, profiel Allocations) toont aan: op iPhone 12 mini verbruikt een lijst van 5000 elementen met LazyVStack 3–5 MB geheugen, terwijl VStack met dezelfde inhoud 150–250 MB verbruikt — 50 keer meer. De initiële rendertijd voor LazyVStack is ~50 ms versus ~800 ms voor VStack op hetzelfde apparaat.

Kies VStack voor statische of korte lijsten (tot 10–15 elementen), en LazyVStack voor dynamische of potentieel lange lijsten. Apple adviseert standaard LazyVStack te gebruiken als je niet zeker bent van de maximale lijstgrootte.

Wanneer VStack nog steeds nodig is

VStack blijft de beste keuze voor statische interfaces: profielscherm, inlogformulier, productkaart — waar het aantal elementen bekend is en niet meer dan 10–15 bedraagt. VStack werkt sneller bij de eerste weergave van een dergelijk aantal elementen, omdat het geen bronnen verspilt aan geometriebewaking en lazy loading. Bovendien werkt VStack correct buiten ScrollView (bijv. binnen ZStack of Group), terwijl LazyVStack zonder ScrollView zijn betekenis verliest.

Wanneer gebruik je LazyVStack en LazyHStack

Luie stacks zijn optimaal voor scenario's met een groot of onvoorspelbaar aantal elementen: social media-feeds, productcatalogi, chatlijsten, mediabibliotheken, gebeurtenislogboeken, beheerpanelen met duizenden records.

Specifieke gebruikssituaties: berichtenlijst in een messenger (tienduizenden berichten), afbeeldingencarrousel in een galerij-app, nieuwsfeed met oneindig scrollen, bestellingenlijst in een webwinkel. LazyHStack is vooral handig voor horizontale carrousels — zoals Stories in Instagram of promotiebanners.

Contra-indicaties: interfaces met verschijningsanimatie van elementen (luie stacks ondersteunen geen overgangen tussen verwijderingsstatussen zonder extra logica), gevallen waarin alle elementen tegelijkertijd zichtbaar moeten zijn (korte checkboxlijst) en wanneer je nauwkeurige controle nodig hebt over celhergebruik (in dit geval kunnen List of Table de voorkeur hebben).

Codevoorbeelden: lijst, raster en secties

Voorbeeld 1: Eenvoudige verticale lijst met LazyVStack

Basisvoorbeeld toont 1000 elementen met minimaal geheugengebruik. Sleutelelementen: ScrollView als scrollcontainer, LazyVStack voor lazy loading, ForEach met identificatie voor gegevensiteratie.

swift
import SwiftUI

struct LazyListExample: View {
    let items = Array(0..<1000)

    var body: some View {
        ScrollView {
            LazyVStack(spacing: 8) {
                ForEach(items, id: \.self) { index in
                    Text("Element #\(index)")
                        .font(.body)
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .padding()
                        .background(Color.gray.opacity(0.1))
                        .cornerRadius(8)
                }
            }
            .padding()
        }
    }
}

De code maakt een ScrollView met daarin een LazyVStack met 8pt tussenruimte tussen elementen. ForEach doorloopt de items-array en maakt Text voor elke index. Dankzij lazy loading bevinden zich van 1000 elementen tegelijkertijd slechts 10–12 zichtbare in het geheugen.

Voorbeeld 2: Secties met pinnedViews

Voorbeeld demonstreert het groeperen van elementen in secties met vastgezette kopteksten, zoals in iOS-contacten. Section definieert koptekst en inhoud, pinnedViews: .sectionHeaders zet de koptekst bovenaan het scherm vast tijdens het scrollen.

swift
import SwiftUI

struct SectionedList: View {
    let cities = ["Moskou", "Londen", "Tokio", "New York", "Parijs"]
    let countries = ["Rusland", "Verenigd Koninkrijk", "Japan", "Verenigde Staten", "Frankrijk"]

    var body: some View {
        ScrollView {
            LazyVStack(pinnedViews: .sectionHeaders) {
                Section(header: Text("Steden").font(.title).bold()) {
                    ForEach(cities, id: \.self) { city in
                        Text(city).padding(8)
                    }
                }
                Section(header: Text("Landen").font(.title).bold()) {
                    ForEach(countries, id: \.self) { country in
                        Text(country).padding(8)
                    }
                }
            }
        }
    }
}

Vastgezette kopteksten (.sectionHeaders) gedragen zich als section headers in UITableView: tijdens het scrollen van een sectie "plakt" de koptekst aan de bovenrand van het scherm totdat de hele sectie verdwijnt, waarna deze wordt vervangen door de koptekst van de volgende sectie. pinnedViews kunnen worden gecombineerd: .sectionHeaders en .sectionFooters tegelijkertijd.

Voorbeeld 3: Horizontale carrousel met LazyHStack

LazyHStack wordt gebruikt voor horizontaal scrollen — afbeeldingencarrousels, horizontale categorielijsten. De parameter alignment: .top lijnt elementen uit aan de bovenrand.

swift
import SwiftUI

struct HorizontalCarousel: View {
    let colors: [Color] = [.red, .blue, .green, .orange, .purple, .pink]

    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            LazyHStack(spacing: 16, alignment: .top) {
                ForEach(0..<100, id: \.self) { index in
                    RoundedRectangle(cornerRadius: 12)
                        .fill(colors[index % colors.count])
                        .frame(width: 150, height: 200)
                        .overlay(Text("\(index + 1)").foregroundColor(.white).bold())
                }
            }
            .padding(.horizontal)
        }
        .frame(height: 220)
    }
}

De code maakt een horizontale ScrollView met LazyHStack. Van de 100 rechthoeken worden er tegelijkertijd 2–3 weergegeven (afhankelijk van schermbreedte en elementgrootte). Bij scrollen naar links worden nieuwe elementen lui geladen. De containerhoogte is vast (220pt) om oneindige hoogte bij horizontaal scrollen te voorkomen.

PinnedViews en secties in luie stacks

PinnedScrollableViews — configuratieoptie van LazyVStack en LazyHStack die het vastzetten van sectiekopteksten en -voetteksten tijdens het scrollen beheert. Twee waarden worden ondersteund: sectionHeaders (kopteksten plakken aan het begin van de container) en sectionFooters (voetteksten plakken aan het einde).

Het pinned views-mechanisme werkt alleen binnen een Section-container genest in LazyVStack. Elke sectie heeft een koptekst en/of voettekst die automatisch het plakgedrag krijgen. SwiftUI volgt de positie van elke sectie ten opzichte van de ScrollView-grenzen en schakelt de zichtbaarheid van het vastgezette element bij overgang tussen secties.

Belangrijk: pinnedViews verhogen de complexiteit van de lay-outberekening omdat SwiftUI constant moet herbereken welke koptekst momenteel is vastgezet. Gebruik pinnedViews alleen wanneer de functionaliteit echt nodig is — voor eenvoudige lijsten zonder secties kun je deze parameter beter weglaten. Apple adviseert in zijn documentatie (Human Interface Guidelines, 2024) het gebruik van vastgezette kopteksten voor alfabetische indexen en groepering op datum.

Tips voor optimalisatie van luie stacks

Correct gebruik van identificaties — de belangrijkste factor voor de prestaties van LazyVStack. Elk element in ForEach moet een stabiele unieke id hebben. Het gebruik van \.self met primitieve typen (Int, String) is toegestaan, maar voor gegevensmodellen implementeer je altijd het Identifiable-protocol. Instabiele id's (bijv. elke keer gegenereerde UUID) dwingen SwiftUI om alle weergaven opnieuw te maken bij elke update.

Vermijd zware berekeningen in de body van elk element van de stack. Als een element complexe lay-out of gegevensverwerking bevat — verplaats de logica naar een aparte weergavestructuur met eigen lazy loading. Gebruik EquatableView om onnodige hertekeneningen te voorkomen wanneer de gegevens van het element niet zijn gewijzigd.

Voor afbeeldingen in LazyVStack moet je asynchroon laden (AsyncImage) of caching via Kingfisher/Nuke toepassen. Elk element dat op het scherm verschijnt, mag niet synchroon een afbeelding laden — dit veroorzaakt haperingen tijdens het scrollen (jank). Volgens WWDC Session 10031 is de optimale buffergrootte voor prefetching 3–5 schermen vooruit en achteruit vanaf de huidige positie.

Meet prestaties via Xcode Instruments met het SwiftUI-profiel. Let op de metrieken: aantal body-evaluaties, allocaties en framesnelheid (FPS). Streefwaarden: FPS > 55 tijdens scrollen, rendertijd van één element < 1 ms.

Veelgestelde vragen

Wat is het verschil tussen LazyVStack en List in SwiftUI?

List biedt ingebouwde mogelijkheden: bewerken door te vegen (swipeActions), verwijderen via .onDelete, herschikken via .onMove, groeperingsstijl .insetGrouped. LazyVStack is een lager niveau-gereedschap zonder ingebouwde ondersteuning voor bewerkingsgebaren. List gebruikt LazyVStack intern, maar voegt een native iOS-tabelstijl toe. Als je een aangepast celontwerp nodig hebt en geen ingebouwde bewerking — kies dan LazyVStack. Als je swipeActions, .onDelete en werken met @FetchRequest nodig hebt — gebruik dan List.

Waarom maakt LazyVStack elementen die niet zichtbaar zijn op het scherm?

Luie stacks gebruiken prefetching — SwiftUI maakt elementen met een kleine vooruitloop (prefetch buffer) om scrollen soepel te laten verlopen. De buffergrootte wordt automatisch aangepast aan de scrollsnelheid en apparaatprestaties. Volgens Apple-profilering is de prefetch buffer meestal 1–3 schermen in de scrollrichting. Als je ziet dat er te veel onzichtbare elementen worden gemaakt, controleer dan of je identificaties hebt die elke keer worden gegenereerd of zware berekeningen in de weergave-initialisator.

Kan ik LazyVStack in VStack nesten of andersom?

Ja, maar met beperkingen. LazyVStack in VStack nesten is zinloos — de buitenste VStack maakt alle elementen van de binnenste LazyVStack onmiddellijk, waardoor lazy loading teniet wordt gedaan. VStack in LazyVStack nesten is toegestaan en breekt het luie mechanisme niet. LazyVStack in een andere LazyVStack nesten is acceptabel voor geneste secties, maar houd prestaties in de gaten: elk niveau voegt overhead toe voor geometriebewaking.

Hoe voeg ik scheidingstekens toe tussen elementen van LazyVStack?

SwiftUI biedt geen ingebouwde scheidingstekens voor LazyVStack. Voeg ze handmatig toe: plaats Divider() na elk element in ForEach, of gebruik de modifier .overlay(Divider(), alignment: .bottom) op elk element. Voor aangepaste scheidingstekens teken je Rectangle().frame(height: 1).foregroundColor(.gray.opacity(0.3)).

Samenvatting

  • LazyVStack — verticale luie stack in SwiftUI voor efficiënte weergave van grote lijsten met het maken van elementen bij binnenkomst in het zichtbare gebied.
  • LazyHStack — horizontale equivalent voor carrousels en horizontale lijsten met hetzelfde lazy loading-mechanisme.
  • Verschil met VStack — VStack maakt alle elementen onmiddellijk; luie stacks alleen zichtbare, tot 50× geheugenbesparing op grote datasets.
  • pinnedViews — vastzetten van sectiekopteksten via Section { header: }.sectionHeaders voor het "plak"-effect.
  • Identificatie via id — verplichte voorwaarde: ForEach vereist een unieke stabiele id voor elk element.
  • Prestaties — prefetch buffer 1–3 schermen, EquatableView ter voorkoming van onnodige hertekeneningen, asynchroon laden van afbeeldingen.
  • Gereedschapskeuze — LazyVStack voor aangepaste ontwerpen, List voor ingebouwd bewerken en swipeActions.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook