LazyVStack va LazyHStack SwiftUI-da nima ekanligini bilib oling — iOS, macOS, watchOS va tvOS platformalarida aylanma ro'yxatlar, panjaralar va karusellarni samarali ko'rsatish uchun dangasa steklar. Oddiy VStack va HStack dan farqli o'laroq, dangasa steklar elementlarni faqat ko'rinish maydoniga kirganda yaratadi, bu esa katta ma'lumotlar to'plamlari bilan ishlashda xotira sarfini keskin kamaytiradi. Dangasa steklarning arxitekturasi Layout protokoliga asoslangan va ForEach va ScrollView orqali identifikatsiya bilan integratsiyalashgan.
Asosiy ma'lumotlar
LazyVStack va LazyHStack — SwiftUI-da joylashtirish konteynerlari bo'lib, ular pastki ko'rinishlarni faqat kerak bo'lganda, aylanma maydonda ko'rinadigan bo'lganda yaratadi va ko'rsatadi. LazyVStack elementlarni vertikal (yuqoridan pastga), LazyHStack esa gorizontal (chapdan o'ngga) joylashtiradi.
Ikkala stek Apple tomonidan SwiftUI 2.0 (iOS 14, macOS 11, watchOS 7, tvOS 14) bilan LazyVGrid va LazyHGrid bilan birga taqdim etilgan. Dangasa steklar paydo bo'lishidan oldin dasturchilar katta ro'yxatlar bilan samarali ishlash uchun UITableView va UICollectionView dan UIViewRepresentable orqali foydalanishga majbur edilar. LazyVStack bu ehtiyojni bartaraf etib, avtomatik dangasa yuklash bilan mahalliy SwiftUI interfeysini taqdim etdi.
Apple WWDC Session 10031 (2020) ma'lumotlariga ko'ra, dangasa steklar deferred view creation mexanizmidan foydalanadi: SwiftUI manba ma'lumotlarini (masalan, modellar massivi) saqlaydi va ko'rinish nusxalarini to'g'ridan-to'g'ri ekranda ko'rsatishdan oldin yaratadi. Aylantirishda steklar allaqachon yaratilgan ko'rinishlarni qayta ishlatadi, yangi ajratmalardan qochadi — bu xotira alokatori va Swift tozalagichiga yukni kamaytiradi.
Dangasa steklar bilan ishlash uchun ularni ScrollView ichiga joylashtirish kerak — aylantirishsiz ekran chegaralaridan tashqariga chiqadigan elementlar shunchaki kesiladi, dangasa yaratilmaydi.
LazyVStack da dangasa yuklash mexanizmi geometriyaga asoslangan: SwiftUI har bir pastki ko'rinishning ScrollView konteyneriga nisbatan o'rnini kuzatadi. Element ko'rinadigan maydon chegarasini kesib o'tganda (bir necha nuqtali kichik bufer bilan), tizim uning init funksiyasini chaqiradi va mazmunni ko'rsatadi. Element ekranni tark etganda, SwiftUI ko'rinishni yo'q qiladi, lekin @State orqali saqlanishi mumkin deb belgilangan bo'lsa, holatni saqlab qoladi.
Bu yondashuv VStack dan farq qiladi, bu yerda barcha pastki ko'rinishlar konteyner initsializatsiyasida darhol, ko'rinishidan qat'i nazar yaratiladi. 10 000 elementli ro'yxat uchun VStack xotirada 10 000 ko'rinish nusxasini yaratadi, LazyVStack esa faqat ekranga sig'adiganlarini (odatda 8–15).
LazyVStack uchta konfiguratsiya parametrini qabul qiladi: alignment (HorizontalAlignment — leading, center, trailing), spacing (CGFloat — elementlar orasidagi bo'shliq) va pinnedViews (PinnedScrollableViews — bo'lim sarlavhalarini mahkamlash). LazyHStack bir xil parametrlardan foydalanadi, lekin alignment VerticalAlignment (top, center, bottom) qabul qiladi.
LazyVStack va VStack o'rtasidagi asosiy farq — pastki elementlarni yaratish strategiyasidir. VStack (eager stack) ko'rsatish vaqtida barcha pastki ko'rinishlarning o'lchami va o'rnini hisoblab chiqadi, bu esa uni katta dinamik ro'yxatlar uchun yaroqsiz qiladi. LazyVStack (dangasa stek) yaratishni element ko'rinadigan bo'lguncha kevochtiradi.
1000 matn qatoridan iborat ro'yxat misolida xatti-harakatni taqqoslaylik. VStack barcha 1000 qatorni darhol xotiraga yuklab, har bir qatorning init funksiyasini chaqiradi va unga xotira ajratadi. Bu zaif qurilmalarda (iPhone SE, iPad mini) ishlashning pasayishiga va ekranning ishga tushish vaqtining oshishiga olib keladi. LazyVStack faqat ko'rinadigan 10–12 qatorni yuklaydi, qolganlarini aylantirish paytida yaratadi.
Amaliy test (Xcode Instruments, Allocations profili) ko'rsatadi: iPhone 12 mini da LazyVStack bilan 5000 elementli ro'yxat 3–5 MB xotira sarflaydi, xuddi shu mazmundagi VStack esa 150–250 MB — 50 baravar ko'p. LazyVStack uchun boshlang'ich ko'rsatish vaqti ~50 ms, VStack uchun esa ~800 ms.
Statik yoki qisqa ro'yxatlar (10–15 elementgacha) uchun VStack, dinamik yoki potentsial uzun ro'yxatlar uchun LazyVStack tanlang. Apple ro'yxatning maksimal hajmiga ishonchingiz komil bo'lmasa, standart sifatida LazyVStack dan foydalanishni tavsiya qiladi.
VStack statik interfeyslar uchun eng yaxshi tanlov bo'lib qoladi: profil ekrani, kirish formasi, mahsulot kartasi — elementlar soni ma'lum bo'lgan va 10–15 dan oshmaydigan joylar. VStack bunday miqdordagi elementlarni birinchi ko'rsatishda tezroq ishlaydi, chunki geometriyani kuzatish va dangasa yuklashga resurs sarflamaydi. Bundan tashqari, VStack ScrollView dan tashqarida ham to'g'ri ishlaydi (masalan, ZStack yoki Group ichida), LazyVStack esa ScrollView siz ma'nosini yo'qotadi.
Dangasa steklar optimaldir ko'p sonli yoki oldindan aytib bo'lmaydigan elementlar ssenariylari uchun: ijtimoiy tarmoq lentalari, mahsulot kataloglari, chat ro'yxatlari, media fayl kutubxonalari, hodisa jurnallari, minglab yozuvli boshqaruv panellari.
Aniq foydalanish holatlari: messenjerdagi xabarlar ro'yxati (o'n minglab xabarlar), galereya ilovasidagi rasm karuseli, cheksiz yuklash bilan yangiliklar lentasi, onlayn do'kondagi buyurtmalar ro'yxati. LazyHStack ayniqsa gorizontal karusellar uchun foydali — Instagram Stories yoki reklama bannerlari kabi.
Qarshi ko'rsatmalar: elementlarning paydo bo'lish animatsiyasi bo'lgan interfeyslar (dangasa steklar qo'shimcha mantiqsiz elementni o'chirish holatlari o'rtasida o'tishni qo'llab-quvvatlamaydi), barcha elementlar bir vaqtning o'zida ko'rinadigan bo'lishi kerak bo'lgan holatlar (qisqa belgilash qutisi ro'yxati) va hujayralarni qayta ishlatishni aniq nazorat qilish kerak bo'lganda (bu holda List yoki Table afzal bo'lishi mumkin).
Asosiy misol minimal xotira sarfi bilan 1000 elementni ko'rsatadi. Asosiy elementlar: aylantirish konteyneri sifatida ScrollView, dangasa yuklash uchun LazyVStack, ma'lumotlar bo'ylab iteratsiya uchun identifikatorli ForEach.
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()
}
}
}
Kod ScrollView yaratadi, ichida elementlar orasida 8pt bo'shliq bilan LazyVStack joylashgan. ForEach items massivi bo'ylab o'tadi va har bir indeks uchun Text yaratadi. Dangasa yuklash tufayli 1000 elementdan bir vaqtning o'zida xotirada faqat ko'rinadigan 10–12 tasi bo'ladi.
Misol elementlarni mahkamlangan sarlavhalar bilan bo'limlarga guruhlashni ko'rsatadi, iOS kontaktlaridagi kabi. Section sarlavha va mazmunni belgilaydi, pinnedViews: .sectionHeaders aylantirishda sarlavhani ekranning yuqori qismida mahkamlaydi.
import SwiftUI
struct SectionedList: View {
let cities = ["Moskva", "London", "Tokio", "Nyu-York", "Parij"]
let countries = ["Rossiya", "Buyuk Britaniya", "Yaponiya", "AQSh", "Fransiya"]
var body: some View {
ScrollView {
LazyVStack(pinnedViews: .sectionHeaders) {
Section(header: Text("Shaharlar").font(.title).bold()) {
ForEach(cities, id: \.self) { city in
Text(city).padding(8)
}
}
Section(header: Text("Davlatlar").font(.title).bold()) {
ForEach(countries, id: \.self) { country in
Text(country).padding(8)
}
}
}
}
}
}
Mahkamlangan sarlavhalar (.sectionHeaders) UITableView dagi section headers kabi harakat qiladi: bo'lim aylantirilganda sarlavha butun bo'lim yo'qolguncha ekranning yuqori chetiga "yopishadi", keyin uni keyingi bo'lim sarlavhasi almashtiradi. pinnedViews ni birlashtirish mumkin: .sectionHeaders va .sectionFooters bir vaqtning o'zida.
LazyHStack gorizontal aylantirish uchun ishlatiladi — rasm karusellari, gorizontal kategoriya ro'yxatlari. alignment: .top parametri elementlarni yuqori chetiga tekislaydi.
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)
}
}
Kod LazyHStack bilan gorizontal ScrollView yaratadi. 100 to'rtburchakdan bir vaqtning o'zida 2–3 tasi ko'rsatiladi (ekran kengligi va element o'lchamiga qarab). Chapga aylantirganda yangi elementlar dangasa yuklanadi. Konteyner balandligi doimiy (220pt), gorizontal aylantirishda cheksiz balandlikning oldini olish uchun.
PinnedScrollableViews — LazyVStack va LazyHStack ning aylantirishda bo'lim sarlavhalari va pastki qismlarini mahkamlashni boshqaradigan konfiguratsiya opsiyasi. Ikki qiymat qo'llab-quvvatlanadi: sectionHeaders (sarlavhalar konteyner boshiga yopishadi) va sectionFooters (pastki qismlar oxiriga yopishadi).
Pinned views mexanizmi faqat LazyVStack ga joylashtirilgan Section konteyneri ichida ishlaydi. Har bir Section avtomatik ravishda yopishish xususiyatiga ega bo'lgan sarlavha va/yoki pastki qismga ega. SwiftUI har bir bo'limning ScrollView chegaralariga nisbatan o'rnini kuzatadi va bo'limlar o'rtasida o'tishda mahkamlangan elementning ko'rinishini o'zgartiradi.
Muhim: pinnedViews joylashtirish hisobining murakkabligini oshiradi, chunki SwiftUI qaysi sarlavha hozirda mahkamlanganligini doimo qayta hisoblashi kerak. pinnedViews dan faqat funksionallik haqiqatan kerak bo'lganda foydalaning — bo'limsiz oddiy ro'yxatlar uchun bu parametrni qoldirish yaxshiroq. Apple o'z hujjatlarida (Human Interface Guidelines, 2024) alifbo indekslari va sanalar bo'yicha guruhlash uchun mahkamlangan sarlavhalardan foydalanishni tavsiya qiladi.
Identifikatorlardan to'g'ri foydalanish — LazyVStack ishlashining eng muhim omili. ForEach dagi har bir element barqaror noyob id ga ega bo'lishi kerak. Primitiv turlar bilan (Int, String) \.self ishlatish joiz, lekin ma'lumot modellari uchun doimo Identifiable protokolini tatbiq eting. Barqaror bo'lmagan id lar (masalan, har safar yaratiladigan UUID) SwiftUI ni har bir yangilashda barcha ko'rinishlarni qayta yaratishga majbur qiladi.
Stack ning har bir elementi body si ichida og'ir hisoblashlardan qoching. Element murakkab joylashtirish yoki ma'lumotlarni qayta ishlashni o'z ichiga olsa — mantiqni o'z dangasa yuklashiga ega alohida ko'rinish strukturasiga o'tkazing. Element ma'lumotlari o'zgarmaganda keraksiz qayta chizishlarning oldini olish uchun EquatableView dan foydalaning.
LazyVStack ichidagi rasmlar uchun majburiy ravishda asinxron yuklash (AsyncImage) yoki Kingfisher/Nuke orqali keshlashni qo'llang. Ekranda paydo bo'ladigan har bir element sinxron ravishda rasm yuklamasligi kerak — bu aylantirishda qotib qolishlarga (jank) olib keladi. WWDC Session 10031 ma'lumotlariga ko'ra, prefetching uchun optimal bufer hajmi joriy joylashuvdan 3–5 ekran oldinga va orqaga.
Ishlashni Xcode Instruments orqali SwiftUI profili bilan o'lchang. Matrikalarga e'tibor bering: body evaluations, allocations va FPS. Maqsadli ko'rsatkichlar: aylantirishda FPS > 55, bitta elementni ko'rsatish vaqti < 1 ms.
Tez-tez so'raladigan savollar
List o'rnatilgan imkoniyatlarni taqdim etadi: surish orqali tahrirlash (swipeActions), .onDelete bilan o'chirish, .onMove bilan ko'chirish, .insetGrouped guruhlash uslubi. LazyVStack past darajadagi vosita bo'lib, tahrirlash imo-ishoralari uchun o'rnatilgan qo'llab-quvvatlashga ega emas. List ichki LazyVStack dan foydalanadi, lekin mahalliy iOS jadval uslubini qo'shadi. Agar maxsus hujayra dizayni kerak bo'lsa va o'rnatilgan tahrirlash kerak bo'lmasa — LazyVStack ni tanlang. Agar swipeActions, .onDelete va @FetchRequest bilan ishlash kerak bo'lsa — List dan foydalaning.
Dangasa steklar prefetching dan foydalanadi — SwiftUI aylantirishning silliq bo'lishi uchun elementlarni kichik oldindan yuklash buferi (prefetch buffer) bilan yaratadi. Bufer hajmi avtomatik ravishda aylantirish tezligi va qurilma ishlashiga moslashtiriladi. Apple profiling ma'lumotlariga ko'ra, prefetch buffer odatda aylantirish yo'nalishida 1–3 ekranni tashkil qiladi. Agar juda ko'p ko'rinmaydigan elementlar yaratilayotganini ko'rsangiz, har safar yaratiladigan identifikatorlaringiz yoki ko'rinish initida og'ir hisoblashlaringiz borligini tekshiring.
Ha, lekin cheklovlar bilan. LazyVStack ni VStack ga joylashtirish ma'nosiz — tashqi VStack ichki LazyVStack ning barcha elementlarini darhol yaratib, dangasa yuklashni bekor qiladi. VStack ni LazyVStack ga joylashtirish mumkin va bu dangasa mexanizmni buzmaydi. LazyVStack ni boshqa LazyVStack ichiga joylashtirish ichki bo'limlar uchun maqbul, lekin ishlashni kuzating: har bir daraja geometriyani kuzatishga qo'shimcha yuk qo'shadi.
SwiftUI LazyVStack uchun o'rnatilgan ajratgichlarni ta'minlamaydi. Ularni qo'lda qo'shing: ForEach dagi har bir elementdan keyin Divider() joylashtiring yoki har bir elementda .overlay(Divider(), alignment: .bottom) modifieridan foydalaning. Maxsus ajratgichlar uchun Rectangle().frame(height: 1).foregroundColor(.gray.opacity(0.3)) chizing.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.