LazyVStack: mik a függőleges és vízszintes lusta vermek a SwiftUI-ben

Szerző: IT Sectr Megjelenés: 2026-02-24 Olvasási idő: 10 perc

Ismerje meg, mik azok a LazyVStack és LazyHStack a SwiftUI-ben — lusta vermek a görgethető listák, rácsok és körhinták hatékony megjelenítéséhez iOS, macOS, watchOS és tvOS rendszereken. A szokásos VStack és HStack vermekkel ellentétben a lusta vermek csak akkor hozzák létre az elemeket, amikor azok megjelennek a látható területen, ami kritikusan csökkenti a memóriahasználatot nagy adathalmazokkal való munka során. A lusta vermek architektúrája a Layout protokollon alapul, és integrálva van a ForEach és ScrollView általi azonosítással.

Főbb pontok

  • Lusta betöltés — A LazyVStack és LazyHStack csak akkor hozza létre a gyermek nézeteket, amikor azok belépnek a ScrollView látható területére, ezzel memóriát takarítva meg és javítva az FPS-t görgetés közben.
  • Azonosítás id-n keresztül — a lusta konténerekkel való helyes munkához a ForEach minden elemének egyedi azonosítóval kell rendelkeznie (id: \.self vagy Identifiable protokoll).
  • pinnedViews — a paraméter lehetővé teszi a szekciófejlécek (sectionHeaders, sectionFooters) rögzítését a képernyő tetején vagy alján görgetés közben, hasonlóan az UITableView section headers-hez.
  • spacing és alignment — mindkét verem egyedi távolságot és igazítást fogad el az elemek között (leading, center, trailing a VStack esetében; top, center, bottom a HStack esetében).
  • Beágyazás GridItem-mel — A LazyVGrid és LazyHGrid ugyanazon lusta betöltési elven alapul, de oszlopok és sorok támogatásával a GridItem-en keresztül.

Mik azok a LazyVStack és LazyHStack?

LazyVStack és LazyHStack — elrendezés konténerek a SwiftUI-ben, amelyek csak szükség esetén hozzák létre és jelenítik meg a gyermek nézeteket, amikor azok láthatóvá válnak a görgethető területen. A LazyVStack függőlegesen (fentről lefelé), a LazyHStack pedig vízszintesen (balról jobbra) helyezi el az elemeket.

Mindkét vermet az Apple mutatta be a SwiftUI 2.0-ban (iOS 14, macOS 11, watchOS 7, tvOS 14) a LazyVGrid és LazyHGrid vermekkel együtt. A lusta vermek megjelenése előtt a fejlesztők kénytelenek voltak UITableView és UICollectionView használni UIViewRepresentable-en keresztül a nagy listákkal való hatékony munkához. A LazyVStack megszüntette ezt a szükségletet, natív SwiftUI felületet biztosítva automatikus lusta betöltéssel.

Az Apple WWDC Session 10031 (2020) adatai szerint a lusta vermek a deferred view creation mechanizmust használják: a SwiftUI tárolja a forrásadatokat (pl. modellek tömbjét), és közvetlenül a képernyőn történő megjelenítés előtt hozza létre a nézet példányokat. Görgetés közben a vermek újrahasznosítják a már létrehozott nézeteket, elkerülve az új allokációkat — ez csökkenti a memória allokátor és a Swift garbage collector terhelését.

A lusta vermekkel való munkához helyezze őket egy ScrollView belsejébe — görgetés nélkül a képernyő határain túlnyúló elemek egyszerűen le lesznek vágva, nem lustán létrehozva.

Hogyan működik a lusta betöltés

A lusta betöltés mechanizmusa a LazyVStack-ben geometrián alapul: a SwiftUI követi az egyes gyermek nézetek pozícióját a ScrollView konténerhez képest. Amikor egy elem átlépi a látható terület határát (egy kis, néhány pontos pufferrel), a rendszer meghívja az inicializálóját és megjeleníti a tartalmat. Amikor az elem elhagyja a képernyőt, a SwiftUI megsemmisíti a nézetet, de megtartja az állapotot @State-en keresztül, ha az megőrizhetőként van megjelölve.

Ez a megközelítés eltér a VStack-től, ahol az összes gyermek nézet azonnal létrejön a konténer inicializálásakor, függetlenül azok láthatóságától. Egy 10 000 elemből álló lista esetén a VStack 10 000 nézetpéldányt hoz létre a memóriában, míg a LazyVStack csak azokat, amelyek elférnek a képernyőn (általában 8–15).

Méretek és igazítás

A LazyVStack három konfigurációs paramétert fogad el: alignment (HorizontalAlignment — leading, center, trailing), spacing (CGFloat — távolság az elemek között) és pinnedViews (PinnedScrollableViews — szekciófejlécek rögzítése). A LazyHStack ugyanazokat a paramétereket használja, de az alignment VerticalAlignment (top, center, bottom) értéket fogad el.

Különbségek a LazyVStack és VStack között: teljesítmény és memória

A fő különbség a LazyVStack és VStack között — a gyermek elemek létrehozásának stratégiája. A VStack (eager stack) kiszámítja az összes gyermek nézet méretét és pozícióját a megjelenítés pillanatában, ami alkalmassá teszi nagy dinamikus listákhoz. A LazyVStack (lusta verem) elhalasztja a létrehozást addig, amíg az elem láthatóvá nem válik.

Hasonlítsuk össze a viselkedést egy 1000 szövegsorból álló lista példáján. A VStack azonnal betölti mind a 1000 sort a memóriába, meghívva minden sor inicializálóját és memóriát foglalva hozzá. Ez gyengébb eszközökön (iPhone SE, iPad mini) teljesítménycsökkenéshez és a képernyő indítási idejének növekedéséhez vezet. A LazyVStack csak a látható 10–12 sort tölti be, a többit görgetés közben hozza létre.

Egy gyakorlati teszt (Xcode Instruments, Allocations profil) azt mutatja: iPhone 12 mini készüléken egy 5000 elemből álló lista LazyVStackkel 3–5 MB memóriát fogyaszt, míg ugyanaz a tartalom VStackkel — 150–250 MB, ami 50-szer több. A kezdeti megjelenítési idő a LazyVStack esetében ~50 ms, szemben a VStack ~800 ms-ával ugyanazon az eszközön.

Válassza a VStack-et statikus vagy rövid listákhoz (10–15 elemig), a LazyVStack-et pedig dinamikus vagy potenciálisan hosszú listákhoz. Az Apple alapértelmezés szerint a LazyVStack használatát ajánlja, ha nem biztos a lista maximális méretében.

Mikor van még szükség a VStack-re

A VStack továbbra is a legjobb választás statikus felületekhez: profilképernyő, bejelentkezési űrlap, termékkártya — ahol az elemek száma ismert és nem haladja meg a 10–15-öt. A VStack gyorsabban működik az ilyen mennyiségű elem első megjelenítésekor, mivel nem pazarol erőforrásokat geometria követésére és lusta betöltésre. Ezenkívül a VStack a ScrollView-n kívül is helyesen működik (pl. ZStack vagy Group belsejében), míg a LazyVStack ScrollView nélkül értelmét veszti.

Mikor használjuk a LazyVStack és LazyHStack vermeket

A lusta vermek optimálisak nagy vagy kiszámíthatatlan számú elemű forgatókönyvekhez: közösségi média hírfolyamok, termékkatalógusok, chat listák, médiafájl könyvtárak, eseménynaplók, adminisztrációs panelek több ezer rekorddal.

Konkrét felhasználási esetek: üzenetlista egy messengeren (több tízezer üzenet), képkörhinta egy galéria alkalmazásban, hírfolyam végtelen betöltéssel, rendelési lista egy webáruházban. A LazyHStack különösen hasznos vízszintes körhintákhoz — mint az Instagram Stories vagy promóciós bannerek.

Ellenjavallatok: elemek megjelenési animációjával rendelkező felületek (a lusta vermek nem támogatják az elemeltávolítási állapotok közötti átmeneteket további logika nélkül), olyan esetek, amikor minden elemnek egyszerre kell láthatónak lennie (rövid jelölőnégyzet lista), és amikor pontos vezérlésre van szükség a cellák újrahasznosítása felett (ebben az esetben a List vagy Table lehet előnyösebb).

Kódpéldák: lista, rács és szekciók

1. példa: Egyszerű függőleges lista LazyVStackkel

Az alap példa 1000 elemet jelenít meg minimális memóriahasználattal. Kulcselemek: ScrollView görgető konténerként, LazyVStack a lusta betöltéshez, ForEach azonosítóval az adatok bejárásához.

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()
        }
    }
}

A kód létrehoz egy ScrollView-t, benne egy LazyVStackkel 8pt távolsággal az elemek között. A ForEach végigmegy az items tömbön, és Text-et hoz létre minden indexhez. A lusta betöltésnek köszönhetően 1000 elemből egyszerre csak a látható 10–12 van a memóriában.

2. példa: Szekciók pinnedViews-szal

A példa az elemek szekciókba csoportosítását mutatja rögzített fejlécekkel, mint az iOS névjegyekben. A Section meghatározza a fejlécet és a tartalmat, a pinnedViews: .sectionHeaders rögzíti a fejlécet a képernyő tetején görgetés közben.

swift
import SwiftUI

struct SectionedList: View {
    let cities = ["Moszkva", "London", "Tokió", "New York", "Párizs"]
    let countries = ["Oroszország", "Egyesült Királyság", "Japán", "USA", "Franciaország"]

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

A rögzített fejlécek (.sectionHeaders) úgy viselkednek, mint az UITableView section headers: a szekció görgetésekor a fejléc „hozzátapad" a képernyő felső széléhez, amíg a teljes szekció el nem tűnik, majd a következő szekció fejléce váltja fel. A pinnedViews kombinálható: .sectionHeaders és .sectionFooters egyszerre.

3. példa: Vízszintes körhinta LazyHStackkel

A LazyHStack vízszintes görgetéshez használatos — képkörhinták, vízszintes kategória listák. A alignment: .top paraméter a felső szélhez igazítja az elemeket.

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)
    }
}

A kód létrehoz egy vízszintes ScrollView-t LazyHStackkel. 100 téglalapból egyszerre 2–3 jelenik meg (a képernyő szélességétől és az elemek méretétől függően). Balra görgetve az új elemek lustán töltődnek be. A konténer magassága rögzített (220pt) a végtelen magasság elkerülése érdekében vízszintes görgetésnél.

PinnedViews és szekciók a lusta vermekben

PinnedScrollableViews — a LazyVStack és LazyHStack konfigurációs opciója, amely a szekciófejlécek és -láblécek rögzítését kezeli görgetés közben. Két érték támogatott: sectionHeaders (fejlécek a konténer elejéhez tapadnak) és sectionFooters (láblécek a végéhez tapadnak).

A pinned views mechanizmus csak a LazyVStack-be ágyazott Section konténeren belül működik. Minden Section rendelkezik fejléccel és/vagy lábléccel, amelyek automatikusan megszerzik a tapadási viselkedést. A SwiftUI követi az egyes szekciók pozícióját a ScrollView határaihoz képest, és átkapcsolja a rögzített elem láthatóságát a szekciók közötti átmenet során.

Fontos: a pinnedViews növeli az elrendezés számításának komplexitását, mivel a SwiftUI-nak folyamatosan újra kell számolnia, hogy melyik fejléc van éppen rögzítve. Csak akkor használja a pinnedViews-t, ha a funkcióra valóban szükség van — egyszerű, szekciók nélküli listák esetén jobb kihagyni ezt a paramétert. Az Apple a dokumentációjában (Human Interface Guidelines, 2024) a rögzített fejlécek használatát ajánlja betűrendes indexekhez és dátum szerinti csoportosításhoz.

Tippek a lusta vermek optimalizálásához

Az azonosítók helyes használata — a LazyVStack teljesítményének legfontosabb tényezője. A ForEach minden elemének stabil egyedi id-vel kell rendelkeznie. A \.self használata primitív típusokkal (Int, String) megengedett, de adatmodellek esetén mindig implementálja az Identifiable protokollt. Az instabil id-k (pl. minden alkalommal generált UUID) arra kényszerítik a SwiftUI-t, hogy minden frissítéskor újra létrehozza az összes nézetet.

Kerülje a nehéz számításokat a verem minden elemének body-jában. Ha az elem összetett elrendezést vagy adatfeldolgozást tartalmaz — helyezze át a logikát egy külön nézetstruktúrába saját lusta betöltéssel. Használja a EquatableView-t a szükségtelen újrarajzolások megelőzésére, amikor az elem adatai nem változtak.

A LazyVStack-en belüli képekhez kötelező aszinkron betöltést (AsyncImage) vagy gyorsítótárazást alkalmazni Kingfisher/Nuke segítségével. A képernyőn megjelenő egyes elemek nem tölthetnek be szinkron módon képet — ez görgetési akadásokat (jank) okoz. A WWDC Session 10031 adatai szerint a prefetching optimális puffermérete 3–5 képernyő előre és hátra a jelenlegi pozíciótól.

Mérje a teljesítményt Xcode Instruments segítségével SwiftUI profillal. Figyeljen a metrikákra: body evaluations, allocations és FPS. Célértékek: FPS > 55 görgetés közben, egy elem megjelenítési ideje < 1 ms.

Gyakran Ismételt Kérdések

Mi a különbség a LazyVStack és a List között a SwiftUI-ben?

A List beépített lehetőségeket biztosít: szerkesztés húzással (swipeActions), törlés .onDelete segítségével, áthelyezés .onMove segítségével, csoportosítási stílus .insetGrouped. A LazyVStack egy alacsonyabb szintű eszköz beépített szerkesztési gesztus támogatás nélkül. A List belsőleg LazyVStack-et használ, de natív iOS táblázatstílust ad hozzá. Ha egyedi cellatervezésre van szüksége és nincs szüksége beépített szerkesztésre — válassza a LazyVStack-et. Ha swipeActions, .onDelete és @FetchRequest használatára van szüksége — használja a List-et.

Miért hoz létre a LazyVStack olyan elemeket, amelyek nem láthatók a képernyőn?

A lusta vermek prefetchinget használnak — a SwiftUI egy kis előrehozással (prefetch buffer) hozza létre az elemeket a gördülékeny görgetés érdekében. A puffer mérete automatikusan alkalmazkodik a görgetés sebességéhez és az eszköz teljesítményéhez. Az Apple profilozási adatai szerint a prefetch buffer általában 1–3 képernyő a görgetés irányában. Ha túl sok láthatatlan elem létrehozását észleli, ellenőrizze, hogy vannak-e minden alkalommal generált azonosítók vagy nehéz számítások a nézet inicializálójában.

Beágyazható-e a LazyVStack a VStack-be vagy fordítva?

Igen, de korlátozásokkal. A LazyVStack VStack-be ágyazása értelmetlen — a külső VStack azonnal létrehozza a belső LazyVStack összes elemét, érvénytelenítve a lusta betöltést. A VStack LazyVStack-be ágyazása megengedett, és nem rontja el a lusta mechanizmust. A LazyVStack másik LazyVStack-be ágyazása elfogadható beágyazott szekciókhoz, de figyelje a teljesítményt: minden szint többletterhelést ad a geometria követéséhez.

Hogyan adhatok elválasztót a LazyVStack elemei közé?

A SwiftUI nem biztosít beépített elválasztókat a LazyVStack számára. Adja hozzá őket manuálisan: helyezzen Divider() minden elem után a ForEach-ben, vagy használja a .overlay(Divider(), alignment: .bottom) módosítót minden elemen. Egyedi elválasztókhoz rajzoljon Rectangle().frame(height: 1).foregroundColor(.gray.opacity(0.3)).

Összefoglaló

  • LazyVStack — függőleges lusta verem SwiftUI-ben nagy listák hatékony megjelenítéséhez, az elemek a látható területre lépéskor jönnek létre.
  • LazyHStack — vízszintes megfelelője körhintákhoz és vízszintes listákhoz, ugyanazzal a lusta betöltési mechanizmussal.
  • Különbség a VStack-től — a VStack azonnal létrehozza az összes elemet; a lusta vermek csak a láthatókat, akár 50× memóriát megtakarítva nagy adathalmazokon.
  • pinnedViews — szekciófejlécek rögzítése a Section { header: }.sectionHeaders segítségével a „tapadó" hatásért.
  • Azonosítás id-n keresztül — kötelező feltétel: a ForEach egyedi stabil id-t igényel minden elemhez.
  • Teljesítmény — prefetch buffer 1–3 képernyő, EquatableView a szükségtelen újrarajzolások megelőzésére, aszinkron képbetöltés.
  • Eszközválasztás — LazyVStack egyedi tervekhez, List beépített szerkesztéshez és swipeActions-hoz.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is