LazyVStack: SwiftUI-də şaquli və üfüqi tənbəl yığınlar nədir

Müəllif: IT Sectr Dərc olunub: 2026-02-24 Oxuma vaxtı: 10 dəq

LazyVStack və LazyHStack nədir öyrənin — SwiftUI-də səmərəli siyahılar, şəbəkələr və karusellər yaratmaq üçün tənbəl yığınlar. Adi VStack və HStack-dən fərqli olaraq, tənbəl yığınlar elementləri yalnız görünmə sahəsinə daxil olduqda yaradır ki, bu da böyük verilənlər dəstləri ilə işləyərkən yaddaş istehlakını kritik dərəcədə azaldır. Tənbəl yığınların arxitekturası Layout protokolu əsasında qurulmuşdur və ForEach və ScrollView vasitəsilə identifikasiya ilə inteqrasiya olunmuşdur.

Əsas məqamlar

  • Tənbəl yükləmə — LazyVStack və LazyHStack alt görünüşləri yalnız ScrollView-in görünmə sahəsinə daxil olduqda yaradır, yaddaşa qənaət edir və sürüşdürmə zamanı FPS-i yaxşılaşdırır.
  • id vasitəsilə identifikasiya — tənbəl konteynerlərlə düzgün iş üçün ForEach-dəki hər bir element unikal identifikatora malik olmalıdır (id: \.self və ya Identifiable protokolu).
  • pinnedViews — parametr sürüşdürmə zamanı bölmə başlıqlarını (sectionHeaders, sectionFooters) ekranın yuxarı və ya aşağı hissəsində bərkitməyə imkan verir, UITableView section headers-ə bənzər.
  • spacing və alignment — hər iki yığın elementlər arasında xüsusi boşluq və düzləndirmə qəbul edir (VStack üçün leading, center, trailing; HStack üçün top, center, bottom).
  • GridItem ilə iç-içə yerləşdirmə — LazyVGrid və LazyHGrid eyni tənbəl yükləmə prinsipi əsasında qurulub, lakin GridItem vasitəsilə sütun və sətir dəstəyi ilə.

LazyVStack və LazyHStack nədir?

LazyVStack və LazyHStack — SwiftUI-də yerləşdirmə konteynerləridir ki, alt görünüşləri yalnız sürüşdürülən sahədə görünən olduqda yaradır və göstərir. LazyVStack elementləri şaquli (yuxarıdan aşağıya), LazyHStack isə üfüqi (soldan sağa) yerləşdirir.

Hər iki yığın Apple tərəfindən SwiftUI 2.0 (iOS 14, macOS 11, watchOS 7, tvOS 14) ilə LazyVGrid və LazyHGrid ilə birlikdə təqdim edilmişdir. Tənbəl yığınlar meydana çıxmamışdan əvvəl proqramçılar böyük siyahılarla səmərəli işləmək üçün UITableView və UICollectionView-dən UIViewRepresentable vasitəsilə istifadə etməli idilər. LazyVStack bu ehtiyacı aradan qaldıraraq avtomatik tənbəl yükləmə ilə doğma SwiftUI interfeysi təqdim etdi.

Apple WWDC Session 10031 (2020) məlumatlarına görə, tənbəl yığınlar deferred view creation mexanizmindən istifadə edir: SwiftUI mənbə məlumatlarını (məsələn, modellər massivi) saxlayır və görünüş nümunələrini birbaşa ekranda göstərilmədən əvvəl yaradır. Sürüşdürmə zamanı yığınlar artıq yaradılmış görünüşləri təkrar istifadə edir, yeni allocasiyalardan qaçınır — bu yaddaş alokatoru və Swift təmizləyicisinə yükü azaldır.

Tənbəl yığınlarla işləmək üçün onları ScrollView-in içərisinə yerləşdirmək lazımdır — sürüşdürmə olmadan ekran hüdudlarından kənara çıxan elementlər sadəcə kəsiləcək, tənbəl yaradılmayacaq.

Tənbəl yükləmə necə işləyir

LazyVStack-də tənbəl yükləmə mexanizmi həndəsəyə əsaslanır: SwiftUI hər bir alt görünüşün ScrollView konteynerinə nisbətən mövqeyini izləyir. Element görünən sahənin həddini keçdikdə (bir neçə punktluq kiçik buferlə), sistem onun inizializatorunu çağırır və məzmunu göstərir. Element ekranı tərk etdikdə, SwiftUI görünüşü məhv edir, lakin @State vasitəsilə saxlanıla bilən kimi qeyd olunubsa, vəziyyəti qoruyur.

Bu yanaşma VStack-dən fərqlənir, burada bütün alt görünüşlər konteynerin inizializasiyası zamanı dərhal, görünməsindən asılı olmayaraq yaradılır. 10 000 elementlik siyahı üçün VStack yaddaşda 10 000 görünüş nümunəsi yaradacaq, LazyVStack isə yalnız ekrana sığanları (adətən 8–15).

Ölçülər və düzləndirmə

LazyVStack üç konfiqurasiya parametri qəbul edir: alignment (HorizontalAlignment — leading, center, trailing), spacing (CGFloat — elementlər arasında boşluq) və pinnedViews (PinnedScrollableViews — bölmə başlıqlarının bərkidilməsi). LazyHStack eyni parametrlərdən istifadə edir, lakin alignment VerticalAlignment (top, center, bottom) qəbul edir.

LazyVStack və VStack arasında fərqlər: performans və yaddaş

LazyVStack və VStack arasında əsas fərq — alt elementlərin yaradılması strategiyasıdır. VStack (eager stack) göstərilmə anında bütün alt görünüşlərin ölçüsünü və mövqeyini hesablayır ki, bu da onu böyük dinamik siyahılar üçün yararsız edir. LazyVStack (lazy stack) yaratmanı element görünən olana qədər təxirə salır.

1000 mətn sətirindən ibarət siyahı nümunəsində davranışı müqayisə edək. VStack bütün 1000 sətiri dərhal yaddaşa yükləyərək hər sətrin inizializatorunu çağıracaq və ona yaddaş ayıracaq. Bu, zəif cihazlarda (iPhone SE, iPad mini) performansın düşməsinə və ekranın işə düşmə vaxtının artmasına səbəb olur. LazyVStack yalnız görünən 10–12 sətiri yükləyəcək, qalanlarını sürüşdürmə zamanı yaradacaq.

Praktiki test (Xcode Instruments, Allocations profili) göstərir: iPhone 12 mini-də LazyVStack ilə 5000 elementlik siyahı 3–5 MB yaddaş sərf edir, eyni məzmunlu VStack isə 150–250 MB — 50 dəfə çox. LazyVStack üçün ilkin göstərmə müddəti ~50 ms, eyni cihazda VStack üçün ~800 ms təşkil edir.

Statik və ya qısa siyahılar (10–15 elementədək) üçün VStack, dinamik və ya potensial olaraq uzun siyahılar üçün LazyVStack seçin. Apple siyahının maksimum ölçüsünə əmin deyilsinizsə, standart olaraq LazyVStack istifadə etməyi tövsiyə edir.

VStack nə vaxt hələ də lazımdır

VStack statik interfeyslər üçün ən yaxşı seçim olaraq qalır: profil ekranı, giriş forması, məhsul kartı — elementlərin sayı məlum olan və 10–15-i keçməyən yerlər. VStack belə sayda elementlərin ilkin göstərilməsində daha sürətli işləyir, çünki həndəsə izləmə və tənbəl yükləməyə resurs sərf etmir. Bundan əlavə, VStack ScrollView xaricində də düzgün işləyir (məsələn, ZStack və ya Group daxilində), LazyVStack isə ScrollView olmadan mənasını itirir.

LazyVStack və LazyHStack nə vaxt istifadə edilməlidir

Tənbəl yığınlar optimaldır çox sayda və ya gözlənilməz sayda element ssenariləri üçün: sosial media lentləri, məhsul kataloqları, çat siyahıları, media fayl kitabxanaları, hadisə jurnalları, minlərlə qeydi olan idarəetmə panelləri.

Konkret istifadə halları: mesajlaşma proqramında mesaj siyahısı (on minlərlə mesaj), qalereya tətbiqində şəkil karuseli, sonsuz yükləmə ilə xəbər lenti, onlayn mağazada sifariş siyahısı. LazyHStack xüsusilə üfüqi karusellər üçün faydalıdır — Instagram Stories və ya reklam bannerləri kimi.

Əks göstərişlər: elementlərin görünmə animasiyası olan interfeyslər (tənbəl yığınlar əlavə məntiq olmadan element silinmə vəziyyətləri arasında keçidləri dəstəkləmir), bütün elementlərin eyni anda görünməli olduğu hallar (qısa seçim siyahısı) və hüceyrələrin təkrar istifadəsinə dəqiq nəzarətə ehtiyac olduqda (bu halda List və ya Table üstün ola bilər).

Kod nümunələri: siyahı, şəbəkə və bölmələr

Nümunə 1: LazyVStack ilə sadə şaquli siyahı

Əsas nümunə minimal yaddaş sərfiyyatı ilə 1000 elementi göstərir. Əsas elementlər: sürüşdürmə konteyneri kimi ScrollView, tənbəl yükləmə üçün LazyVStack, verilənlər üzərində iterasiya üçün identifikatorlu ForEach.

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

Kod ScrollView yaradır, içərisində elementlər arasında 8pt boşluq olan LazyVStack yerləşdirilib. ForEach items massivi üzərində keçir və hər indeks üçün Text yaradır. Tənbəl yükləmə sayəsində 1000 elementdən eyni anda yaddaşda yalnız görünən 10–12-si olur.

Nümunə 2: pinnedViews ilə bölmələr

Nümunə elementlərin bərkidilmiş başlıqlarla bölmələrə qruplaşdırılmasını göstərir, iOS kontaktlarındakı kimi. Section başlıq və məzmunu müəyyən edir, pinnedViews: .sectionHeaders sürüşdürmə zamanı başlığı ekranın yuxarısında sabitləyir.

swift
import SwiftUI

struct SectionedList: View {
    let cities = ["Moskva", "London", "Tokio", "Nyu-York", "Paris"]
    let countries = ["Rusiya", "Böyük Britaniya", "Yaponiya", "ABŞ", "Fransa"]

    var body: some View {
        ScrollView {
            LazyVStack(pinnedViews: .sectionHeaders) {
                Section(header: Text("Şəhərlər").font(.title).bold()) {
                    ForEach(cities, id: \.self) { city in
                        Text(city).padding(8)
                    }
                }
                Section(header: Text("Ölkələr").font(.title).bold()) {
                    ForEach(countries, id: \.self) { country in
                        Text(country).padding(8)
                    }
                }
            }
        }
    }
}

Bərkidilmiş başlıqlar (.sectionHeaders) UITableView-də section headers kimi davranır: bölmə sürüşdürüldükdə başlıq bütün bölmə yox olana qədər ekranın yuxarı kənarına „yapışır", sonra növbəti bölmənin başlığı onu əvəz edir. pinnedViews birləşdirilə bilər: .sectionHeaders və .sectionFooters eyni anda.

Nümunə 3: LazyHStack ilə üfüqi karusel

LazyHStack üfüqi sürüşdürmə üçün istifadə olunur — şəkil karuselləri, üfüqi kateqoriya siyahıları. alignment: .top parametri elementləri yuxarı kənara düzləndirir.

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

Kod LazyHStack ilə üfüqi ScrollView yaradır. 100 düzbucaqlıdan eyni anda 2–3-ü göstərilir (ekran eni və element ölçüsündən asılı olaraq). Sola sürüşdürdükdə yeni elementlər tənbəl yüklənir. Konteynerin hündürlüyü sabitdir (220pt), üfüqi sürüşdürmədə sonsuz hündürlüyün qarşısını almaq üçün.

Tənbəl yığınlarda PinnedViews və bölmələr

PinnedScrollableViews — LazyVStack və LazyHStack-in sürüşdürmə zamanı bölmə başlıqlarının və altlıqlarının bərkidilməsini idarə edən konfiqurasiya seçimi. İki dəyər dəstəklənir: sectionHeaders (başlıqlar konteynerin əvvəlinə yapışır) və sectionFooters (altlıqlar sona yapışır).

Pinned views mexanizmi yalnız LazyVStack-ə daxil edilmiş Section konteyneri daxilində işləyir. Hər Section-ın başlığı və/və ya altlığı avtomatik olaraq yapışma davranışı əldə edir. SwiftUI hər bölmənin ScrollView hüdudlarına nisbətən mövqeyini izləyir və bölmələr arasında keçid zamanı bərkidilmiş elementin görünməsini dəyişir.

Vacib: pinnedViews yerləşdirmə hesablamasının mürəkkəbliyini artırır, çünki SwiftUI hansı başlığın hazırda bərkidildiyini daim yenidən hesablamalıdır. pinnedViews-dən yalnız funksionallıq həqiqətən lazım olduqda istifadə edin — bölməsiz sadə siyahılar üçün bu parametri buraxmaq daha yaxşıdır. Apple sənədləşməsində (Human Interface Guidelines, 2024) əlifba göstəriciləri və tarixlərə görə qruplaşdırma üçün bərkidilmiş başlıqlardan istifadə etməyi tövsiyə edir.

Tənbəl yığınların optimallaşdırılması üçün məsləhətlər

İdentifikatorlardan düzgün istifadə — LazyVStack performansının ən vacib amilidir. ForEach-dəki hər bir element sabit unikal id-yə malik olmalıdır. Primitiv tiplərlə (Int, String) \.self istifadəsi məqbuldur, lakin məlumat modelləri üçün həmişə Identifiable protokolunu tətbiq edin. Qeyri-sabit id-lər (məsələn, hər dəfə yaradılan UUID) SwiftUI-ni hər yeniləmədə bütün görünüşləri yenidən yaratmağa məcbur edir.

Yığının hər bir elementinin body-si daxilində ağır hesablamalardan qaçının. Element mürəkkəb düzülüş və ya məlumat emalı ehtiva edirsə — məntiqi öz tənbəl yükləməsi olan ayrıca görünüş strukturuna çıxarın. Elementin məlumatları dəyişmədikdə lazımsız yenidən çəkmələrin qarşısını almaq üçün EquatableView istifadə edin.

LazyVStack daxilində şəkillər üçün mütləq asinxron yükləmə (AsyncImage) və ya Kingfisher/Nuke vasitəsilə keşləmə tətbiq edin. Ekranda görünən hər bir element sinxron şəkildə şəkil yükləməməlidir — bu sürüşdürmədə ləngimələrə (jank) səbəb olacaq. WWDC Session 10031 məlumatlarına görə, prefetching üçün optimal bufer ölçüsü cari mövqedən 3–5 ekran irəli və geridir.

Performansı Xcode Instruments ilə SwiftUI profili vasitəsilə ölçün. Metriklərə diqqət yetirin: body evaluations, allocations və FPS. Hədəf göstəriciləri: sürüşdürmə zamanı FPS > 55, bir elementin göstərilmə müddəti < 1 ms.

Tez-tez verilən suallar

SwiftUI-də LazyVStack və List arasında fərq nədir?

List daxili imkanlar təmin edir: sürüşdürmə ilə redaktə (swipeActions), .onDelete ilə silmə, .onMove ilə yerdəyişmə, .insetGrouped qruplaşdırma stili. LazyVStack daha aşağı səviyyəli alətdir, redaktə jestləri üçün daxili dəstək yoxdur. List daxildə LazyVStack-dən istifadə edir, lakin doğma iOS cədvəl stilini əlavə edir. Əgər xüsusi hüceyrə dizaynına ehtiyacınız varsa və daxili redaktə lazım deyilsə — LazyVStack seçin. swipeActions, .onDelete və @FetchRequest ilə iş lazımdırsa — List istifadə edin.

Niyə LazyVStack ekranda görünməyən elementləri yaradır?

Tənbəl yığınlar prefetching istifadə edir — SwiftUI sürüşdürmənin hamar olması üçün elementləri kiçik qabaqcadan yükləmə buferi (prefetch buffer) ilə yaradır. Bufer ölçüsü avtomatik olaraq sürüşdürmə sürətinə və cihazın performansına uyğunlaşdırılır. Apple profilləmə məlumatlarına görə, prefetch buffer adətən sürüşdürmə istiqamətində 1–3 ekran təşkil edir. Əgər çoxlu görünməz element yaradıldığını görürsünüzsə, hər dəfə yaradılan identifikatorlarınızın və ya görünüş inizializatorunda ağır hesablamalarınızın olub olmadığını yoxlayın.

LazyVStack-i VStack-ə və ya əksinə yerləşdirmək olar?

Bəli, lakin məhdudiyyətlərlə. LazyVStack-i VStack-ə yerləşdirmək mənasızdır — xarici VStack daxili LazyVStack-in bütün elementlərini dərhal yaradaraq tənbəl yükləməni ləğv edir. VStack-i LazyVStack-ə yerləşdirmək mümkündür, bu tənbəl mexanizmi pozmur. LazyVStack-i başqa LazyVStack-in içərisinə yerləşdirmək iç-içə bölmələr üçün məqbuldur, lakin performansa nəzarət edin: hər səviyyə həndəsə izləməyə əlavə yük gətirir.

LazyVStack elementləri arasında ayırıcını necə əlavə etmək olar?

SwiftUI LazyVStack üçün daxili ayırıcılar təmin etmir. Onları əl ilə əlavə edin: ForEach-də hər elementdən sonra Divider() yerləşdirin və ya hər elementdə .overlay(Divider(), alignment: .bottom) modifierindən istifadə edin. Xüsusi ayırıcılar üçün Rectangle().frame(height: 1).foregroundColor(.gray.opacity(0.3)) çəkin.

Xülasə

  • LazyVStack — elementləri görünmə sahəsinə daxil olduqda yaradan böyük siyahıların səmərəli göstərilməsi üçün SwiftUI şaquli tənbəl yığını.
  • LazyHStack — eyni tənbəl yükləmə mexanizmi ilə karusellər və üfüqi siyahılar üçün üfüqi analoq.
  • VStack-dən fərq — VStack bütün elementləri dərhal yaradır; tənbəl yığınlar yalnız görünənləri, böyük dəstlərdə 50×-ə qədər yaddaşa qənaət edir.
  • pinnedViews — Section { header: }.sectionHeaders vasitəsilə bölmə başlıqlarının bərkidilməsi, „yapışma" effekti üçün.
  • id vasitəsilə identifikasiya — məcburi şərt: ForEach hər element üçün unikal sabit id tələb edir.
  • Performans — 1–3 ekran prefetch buferi, lazımsız yenidən çəkmələrin qarşısını almaq üçün EquatableView, asinxron şəkil yükləmə.
  • Alət seçimi — xüsusi dizaynlar üçün LazyVStack, daxili redaktə və swipeActions üçün List.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun