LazyVStack: ano ang vertical at horizontal na lazy stack sa SwiftUI

May-akda: IT Sectr Nai-publish: 2026-02-24 Oras ng pagbabasa: 10 min

Alamin kung ano ang LazyVStack at LazyHStack sa SwiftUI — mga lazy stack para sa mahusay na pag-render ng mga nai-scroll na listahan, grid, at carousel sa iOS, macOS, watchOS at tvOS. Hindi tulad ng karaniwang VStack at HStack, ang mga lazy stack ay gumagawa ng mga elemento kapag pumasok lamang sila sa lugar ng visibility, na kritikal na nagbabawas ng paggamit ng memorya kapag nagtatrabaho sa malalaking set ng data. Ang arkitektura ng mga lazy stack ay batay sa protocol na Layout at isinama sa pagkilala sa pamamagitan ng ForEach at ScrollView.

Mga Pangunahing Punto

  • Lazy loading — Ang LazyVStack at LazyHStack ay gumagawa ng mga child view kapag pumasok lamang sila sa visibility area ng ScrollView, nagtitipid ng memorya at nagpapabuti ng FPS habang nag-i-scroll.
  • Pagkilala sa pamamagitan ng id — para sa tamang pagtatrabaho sa mga malalagong container, bawat elemento sa ForEach ay dapat may natatanging identifier (id: \.self o Identifiable protocol).
  • pinnedViews — ang parameter ay nagpapahintulot sa pag-pin ng mga header ng seksyon (sectionHeaders, sectionFooters) sa itaas o ibaba ng screen habang nag-i-scroll, katulad ng UITableView section headers.
  • spacing at alignment — parehong stack ay tumatanggap ng custom na pagitan sa pagitan ng mga elemento at alignment (leading, center, trailing para sa VStack; top, center, bottom para sa HStack).
  • Nesting sa GridItem — Ang LazyVGrid at LazyHGrid ay binuo sa parehong prinsipyo ng lazy loading, ngunit may suporta para sa mga column at row sa pamamagitan ng GridItem.

Ano ang LazyVStack at LazyHStack?

LazyVStack at LazyHStack — mga container ng layout sa SwiftUI na gumagawa at nagpapakita ng mga child view kung kinakailangan, kapag sila ay naging nakikita sa nai-scroll na lugar. Inaayos ng LazyVStack ang mga elemento nang patayo (mula itaas pababa), at LazyHStack — nang pahalang (mula kaliwa pakanan).

Ang parehong stack ay ipinakilala ng Apple sa SwiftUI 2.0 (iOS 14, macOS 11, watchOS 7, tvOS 14) kasama ang LazyVGrid at LazyHGrid. Bago lumitaw ang mga lazy stack, ang mga developer ay napilitang gumamit ng UITableView at UICollectionView sa pamamagitan ng UIViewRepresentable para sa mahusay na pagtatrabaho sa malalaking listahan. Inalis ng LazyVStack ang pangangailangang ito, na nagbibigay ng native SwiftUI interface na may awtomatikong lazy loading.

Ayon sa datos ng Apple WWDC Session 10031 (2020), ang mga lazy stack ay gumagamit ng mekanismong deferred view creation: Iniimbak ng SwiftUI ang source data (hal., array ng mga modelo) at gumagawa ng mga instance ng view bago i-render sa screen. Habang nag-i-scroll, muling ginagamit ng mga stack ang mga nagawa nang view, iniiwasan ang bagong alokasyon — binabawasan nito ang load sa memory allocator at garbage collector ng Swift.

Para magtrabaho sa malalagong stack, kailangan ilagay ang mga ito sa loob ng ScrollView — walang scroll, ang mga elementong lumalabas sa hangganan ng screen ay puputulin lamang, hindi lilikhain nang tamad.

Paano gumagana ang lazy loading

Ang mekanismo ng lazy loading sa LazyVStack ay batay sa geometry: Sinusubaybayan ng SwiftUI ang posisyon ng bawat child view kaugnay ng ScrollView container. Kapag ang elemento ay tumawid sa hangganan ng nakikitang lugar (na may maliit na buffer na ilang puntos), tinatawagan ng system ang initializer nito at nire-render ang nilalaman. Kapag ang elemento ay umalis sa screen, sinisira ng SwiftUI ang view, ngunit pinapanatili ang estado sa pamamagitan ng @State kung minarkahan bilang mapapanatili.

Ang pamamaraang ito ay naiiba mula sa VStack, kung saan ang lahat ng child view ay agad na ginagawa sa pagsisimula ng container, anuman ang kanilang visibility. Para sa isang listahan ng 10,000 elemento, ang VStack ay lilikha ng 10,000 instance ng view sa memorya, habang ang LazyVStack — tanging ang mga kasya sa screen (karaniwan ay 8–15).

Mga sukat at alignment

Ang LazyVStack ay tumatanggap ng tatlong parameter ng configuration: alignment (HorizontalAlignment — leading, center, trailing), spacing (CGFloat — pagitan sa pagitan ng mga elemento) at pinnedViews (PinnedScrollableViews — pag-pin ng mga header ng seksyon). Ang LazyHStack ay gumagamit ng parehong mga parameter, ngunit ang alignment ay tumatanggap ng VerticalAlignment (top, center, bottom).

Mga Pagkakaiba sa pagitan ng LazyVStack at VStack: performance at memorya

Ang pangunahing pagkakaiba sa pagitan ng LazyVStack at VStack — ang estratehiya ng paggawa ng mga child element. Kinakalkula ng VStack (eager stack) ang laki at posisyon ng lahat ng child view sa sandali ng pag-render, na ginagawang hindi angkop para sa malalaking dynamic na listahan. Ang LazyVStack (lazy stack) ay ipinagpapaliban ang paggawa hanggang sa maging nakikita ang elemento.

Ihambing natin ang pag-uugali sa halimbawa ng isang listahan ng 1000 linya ng teksto. Ang VStack ay maglo-load ng lahat ng 1000 linya sa memorya agad, tinatawagan ang initializer ng bawat linya at nag-aalok ng memorya para dito. Ito ay humahantong sa pagbaba ng performance sa mahihinang device (iPhone SE, iPad mini) at pagtaas ng oras ng pagsisimula ng screen. Ang LazyVStack ay maglo-load lamang ng nakikitang 10–12 linya, na gumagawa ng natitira habang nag-i-scroll.

Ang praktikal na pagsubok (gamit ang Xcode Instruments, profile Allocations) ay nagpapakita: sa iPhone 12 mini, ang listahan ng 5000 elemento na may LazyVStack ay kumokonsumo ng 3–5 MB memorya, habang ang VStack na may parehong nilalaman — 150–250 MB, 50 beses na higit pa. Ang paunang oras ng pag-render para sa LazyVStack ay ~50 ms kumpara sa ~800 ms para sa VStack sa parehong device.

Pumili ng VStack para sa static o maikling listahan (hanggang 10–15 elemento), at LazyVStack — para sa anumang dynamic o potensyal na mahabang listahan. Inirerekomenda ng Apple ang paggamit ng LazyVStack bilang default kung hindi ka sigurado sa maximum na laki ng listahan.

Kailan kailangan pa rin ang VStack

Ang VStack ay nananatiling pinakamahusay na pagpipilian para sa static na interfaces: screen ng profile, form ng pag-login, card ng produkto — kung saan ang bilang ng mga elemento ay kilala at hindi lalampas sa 10–15. Mas mabilis gumagana ang VStack sa unang pag-render ng ganoong bilang ng mga elemento, dahil hindi ito gumagamit ng resources sa pagsubaybay ng geometry at lazy loading. Bukod dito, ang VStack ay gumagana nang tama sa labas ng ScrollView (hal., sa loob ng ZStack o Group), habang ang LazyVStack na walang ScrollView ay nawawalan ng kahulugan.

Kailan gagamitin ang LazyVStack at LazyHStack

Ang malalagong stack ay optimal para sa mga senaryo na may malaki o hindi inaasahang bilang ng mga elemento: feed ng social media, katalogo ng produkto, listahan ng chat, library ng media file, log ng kaganapan, panel ng administrasyon na may libu-libong tala.

Mga tiyak na kaso ng paggamit: listahan ng mensahe sa isang messenger (sampu-sampung libong mensahe), carousel ng imahe sa isang gallery app, feed ng balita na may walang katapusang pag-load, listahan ng order sa isang online na tindahan. Ang LazyHStack ay lalong kapaki-pakinabang para sa pahalang na mga carousel — tulad ng Stories sa Instagram o mga banner ng promosyon.

Contraindications: interfaces na may animation ng paglitaw ng elemento (ang malalagong stack ay hindi sumusuporta sa mga transition sa pagitan ng mga estado ng pagtanggal ng elemento nang walang karagdagang logic), mga kaso kung kailan ang lahat ng elemento ay dapat makita nang sabay-sabay (maikling listahan ng checkbox) at kung kailan mo kailangan ng tumpak na kontrol sa muling paggamit ng cell (sa kasong ito, ang List o Table ay maaaring mas gusto).

Mga halimbawa ng code: listahan, grid, at mga seksyon

Halimbawa 1: Simpleng vertical listahan na may LazyVStack

Ang pangunahing halimbawa ay nagpapakita ng 1000 elemento na may minimal na paggamit ng memorya. Mga pangunahing elemento: ScrollView bilang container ng pag-scroll, LazyVStack para sa lazy loading, ForEach na may identifier para sa data iteration.

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

Ang code ay gumagawa ng ScrollView, sa loob nito ay inilagay ang LazyVStack na may 8pt na pagitan sa pagitan ng mga elemento. Ang ForEach ay umiikot sa array ng items at gumagawa ng Text para sa bawat index. Dahil sa lazy loading, mula sa 1000 elemento, sabay-sabay na nasa memorya lamang ang nakikitang 10–12.

Halimbawa 2: Mga seksyon na may pinnedViews

Ang halimbawa ay nagpapakita ng pagpapangkat ng mga elemento sa mga seksyon na may naka-pin na header, tulad ng sa iOS contacts. Section ay tumutukoy sa header at nilalaman, pinnedViews: .sectionHeaders ay nag-pin ng header sa itaas ng screen habang nag-i-scroll.

swift
import SwiftUI

struct SectionedList: View {
    let cities = ["Moscow", "London", "Tokyo", "New York", "Paris"]
    let countries = ["Russia", "United Kingdom", "Japan", "USA", "France"]

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

Ang mga naka-pin na header (.sectionHeaders) ay kumikilos tulad ng section headers sa UITableView: habang nag-i-scroll ng seksyon, ang header ay "dumidikit" sa itaas na gilid ng screen hanggang sa mawala ang buong seksyon, pagkatapos ay pinapalitan ng header ng susunod na seksyon. Ang pinnedViews ay maaaring pagsamahin: .sectionHeaders at .sectionFooters nang sabay-sabay.

Halimbawa 3: Pahalang na carousel na may LazyHStack

Ang LazyHStack ay ginagamit para sa pahalang na pag-scroll — mga carousel ng imahe, pahalang na listahan ng kategorya. Ang parameter na alignment: .top ay nag-aalign ng mga elemento sa itaas na gilid.

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

Ang code ay gumagawa ng pahalang na ScrollView na may LazyHStack. Mula sa 100 rectangle, sabay-sabay na ipinapakita ang 2–3 (depende sa lapad ng screen at laki ng elemento). Kapag nag-scroll pakaliwa, ang mga bagong elemento ay ni-load nang tamad. Ang taas ng container ay fixed (220pt) upang maiwasan ang walang katapusang taas sa pahalang na pag-scroll.

PinnedViews at mga seksyon sa malalagong stack

PinnedScrollableViews — opsyon ng configuration ng LazyVStack at LazyHStack na namamahala sa pag-pin ng mga header at footer ng seksyon habang nag-i-scroll. Dalawang halaga ang sinusuportahan: sectionHeaders (mga header ay dumidikit sa simula ng container) at sectionFooters (mga footer ay dumidikit sa dulo).

Ang mekanismo ng pinned views ay gumagana lamang sa loob ng Section container na naka-nest sa LazyVStack. Bawat seksyon ay may header at/o footer na awtomatikong nakakakuha ng pag-uugali ng pagdikit. Sinusubaybayan ng SwiftUI ang posisyon ng bawat seksyon kaugnay ng mga hangganan ng ScrollView at inililipat ang visibility ng naka-pin na elemento sa paglipat sa pagitan ng mga seksyon.

Mahalaga: pinapataas ng pinnedViews ang complexity ng pagkalkula ng layout, dahil ang SwiftUI ay dapat patuloy na magkalkula kung aling header ang kasalukuyang naka-pin. Gamitin lamang ang pinnedViews kapag talagang kailangan ang functionality — para sa simpleng listahan na walang seksyon, mas mabuting iwanan ang parameter na ito. Inirerekomenda ng Apple sa dokumentasyon nito (Human Interface Guidelines, 2024) ang paggamit ng mga naka-pin na header para sa mga alpabetikong index at pagpapangkat ayon sa petsa.

Mga tip para sa pag-optimize ng malalagong stack

Tamang paggamit ng mga identifier — ang pinakamahalagang factor ng performance ng LazyVStack. Bawat elemento sa ForEach ay dapat may matatag na natatanging id. Ang paggamit ng \.self na may primitive na uri (Int, String) ay pinapayagan, ngunit para sa mga modelo ng data ay laging ipatupad ang Identifiable protocol. Ang hindi matatag na id (hal., UUID na nabubuo sa bawat pagkakataon) ay pumipilit sa SwiftUI na muling likhain ang lahat ng view sa bawat update.

Iwasan ang mabibigat na kalkulasyon sa loob ng body ng bawat elemento ng stack. Kung ang elemento ay naglalaman ng kumplikadong layout o pagproseso ng data — ilipat ang logic sa isang hiwalay na istraktura ng view na may sariling lazy loading. Gamitin ang EquatableView upang maiwasan ang hindi kinakailangang pag-redrawing kapag hindi nagbago ang data ng elemento.

Para sa mga imahe sa loob ng LazyVStack, mandatoryong mag-apply ng asynchronous loading (AsyncImage) o caching sa pamamagitan ng Kingfisher/Nuke. Bawat elemento na lumalabas sa screen ay hindi dapat mag-load ng imahe nang sabay-sabay — ito ay magdudulot ng pagkabulol sa pag-scroll (jank). Ayon sa datos ng WWDC Session 10031, ang optimal na laki ng buffer para sa prefetching ay 3–5 screen pasulong at pabalik mula sa kasalukuyang posisyon.

Sukatin ang performance sa pamamagitan ng Xcode Instruments na may SwiftUI profile. Bigyang-pansin ang metrics: bilang ng body evaluations, allocations at frame rate (FPS). Target na halaga: FPS > 55 habang nag-i-scroll, oras ng pag-render ng isang elemento < 1 ms.

Mga Madalas Itanong

Ano ang pagkakaiba sa pagitan ng LazyVStack at List sa SwiftUI?

Ang List ay nagbibigay ng mga built-in na kakayahan: pag-edit sa pamamagitan ng pag-swipe (swipeActions), pagtanggal sa pamamagitan ng .onDelete, paglipat sa pamamagitan ng .onMove, estilo ng pagpapangkat .insetGrouped. Ang LazyVStack ay isang mas mababang antas na tool na walang built-in na suporta para sa mga kilos ng pag-edit. Ang List ay gumagamit ng LazyVStack sa loob, ngunit nagdaragdag ng native na estilo ng table ng iOS. Kung kailangan mo ng custom na disenyo ng cell at hindi mo kailangan ng built-in na pag-edit — piliin ang LazyVStack. Kung kailangan mo ng swipeActions, .onDelete at pagtatrabaho sa @FetchRequest — gamitin ang List.

Bakit gumagawa ang LazyVStack ng mga elementong hindi nakikita sa screen?

Ang malalagong stack ay gumagamit ng prefetching — Gumagawa ang SwiftUI ng mga elemento na may maliit na advance (prefetch buffer) upang maging maayos ang pag-scroll. Ang laki ng buffer ay awtomatikong inaayos sa bilis ng pag-scroll at performance ng device. Ayon sa profiling data ng Apple, ang prefetch buffer ay karaniwang 1–3 screen sa direksyon ng pag-scroll. Kung nakikita mong gumagawa ng masyadong maraming invisible na elemento, suriin kung mayroon kang mga identifier na nabubuo sa bawat pagkakataon o mabibigat na kalkulasyon sa initializer ng view.

Maaari bang i-nest ang LazyVStack sa VStack o vice versa?

Oo, ngunit may mga limitasyon. Ang pag-nest ng LazyVStack sa VStack ay walang saysay — ang panlabas na VStack ay lilikha ng lahat ng elemento ng panloob na LazyVStack agad, na kinakansela ang lazy loading. Ang pag-nest ng VStack sa LazyVStack ay pinapayagan at hindi sinisira ang lazy mechanism. Ang pag-nest ng LazyVStack sa isa pang LazyVStack ay katanggap-tanggap para sa nested na mga seksyon, ngunit bantayan ang performance: bawat antas ay nagdaragdag ng overhead para sa pagsubaybay ng geometry.

Paano magdagdag ng separator sa pagitan ng mga elemento ng LazyVStack?

Ang SwiftUI ay hindi nagbibigay ng built-in na separator para sa LazyVStack. Idagdag ang mga ito nang manu-mano: ilagay ang Divider() pagkatapos ng bawat elemento sa ForEach, o gamitin ang modifier na .overlay(Divider(), alignment: .bottom) sa bawat elemento. Para sa custom na separator, gumuhit ng Rectangle().frame(height: 1).foregroundColor(.gray.opacity(0.3)).

Buod

  • LazyVStack — vertical lazy stack ng SwiftUI para sa mahusay na pag-render ng malalaking listahan na may paggawa ng elemento kapag pumapasok sa visibility area.
  • LazyHStack — horizontal counterpart para sa mga carousel at horizontal na listahan na may parehong mekanismo ng lazy loading.
  • Pagkakaiba sa VStack — Ang VStack ay gumagawa ng lahat ng elemento agad; ang malalagong stack ay nakikita lamang, nagtitipid ng hanggang 50× memorya sa malalaking set.
  • pinnedViews — pag-pin ng mga header ng seksyon sa pamamagitan ng Section { header: }.sectionHeaders para sa "dumidikit" na epekto.
  • Pagkilala sa pamamagitan ng id — mandatoryong kondisyon: Ang ForEach ay nangangailangan ng natatanging matatag na id para sa bawat elemento.
  • Performance — prefetch buffer 1–3 screen, EquatableView para maiwasan ang hindi kinakailangang pag-redrawing, asynchronous loading ng imahe.
  • Pagpili ng tool — LazyVStack para sa custom na disenyo, List para sa built-in na pag-edit at swipeActions.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din