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
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.
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).
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).
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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
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.
Basahin din