जानें कि SwiftUI में LazyVStack और LazyHStack क्या हैं — iOS, macOS, watchOS और tvOS पर स्क्रॉल करने योग्य सूचियों, ग्रिड और कैरोसेल के कुशल रेंडरिंग के लिए लेज़ी स्टैक। सामान्य VStack और HStack के विपरीत, लेज़ी स्टैक तत्व केवल तभी बनाते हैं जब वे दृश्य क्षेत्र में दिखाई देते हैं, जो बड़े डेटा सेट के साथ काम करते समय मेमोरी खपत को गंभीर रूप से कम करता है। लेज़ी स्टैक की आर्किटेक्चर Layout प्रोटोकॉल पर आधारित है और ForEach और ScrollView के माध्यम से पहचान के साथ एकीकृत है।
मुख्य बिंदु
LazyVStack और LazyHStack SwiftUI में लेआउट कंटेनर हैं जो चाइल्ड व्यू को केवल आवश्यकतानुसार बनाते और प्रदर्शित करते हैं, जब वे स्क्रॉल करने योग्य क्षेत्र में दृश्यमान होते हैं। LazyVStack तत्वों को वर्टिकल रूप से (ऊपर से नीचे) व्यवस्थित करता है, जबकि LazyHStack उन्हें हॉरिज़ॉन्टल रूप से (बाएँ से दाएँ) व्यवस्थित करता है।
दोनों स्टैक Apple द्वारा SwiftUI 2.0 (iOS 14, macOS 11, watchOS 7, tvOS 14) में LazyVGrid और LazyHGrid के साथ पेश किए गए थे। लेज़ी स्टैक के आने से पहले, डेवलपर्स को बड़ी सूचियों के साथ कुशलतापूर्वक काम करने के लिए UIViewRepresentable के माध्यम से UITableView और UICollectionView का उपयोग करना पड़ता था। LazyVStack ने स्वचालित लेज़ी लोडिंग के साथ एक मूल SwiftUI इंटरफ़ेस प्रदान करके इस आवश्यकता को समाप्त कर दिया।
Apple WWDC Session 10031 (2020) के अनुसार, लेज़ी स्टैक विलंबित व्यू निर्माण तंत्र का उपयोग करते हैं: SwiftUI स्रोत डेटा (जैसे, मॉडल की एक सरणी) संग्रहीत करता है और स्क्रीन पर रेंडर करने से ठीक पहले व्यू इंस्टेंस बनाता है। स्क्रॉल करते समय, स्टैक पहले से बनाए गए व्यू का पुन: उपयोग करते हैं, नए आवंटन से बचते हैं — यह मेमोरी आवंटकर्ता और Swift के कचरा संग्रहकर्ता पर लोड को कम करता है।
लेज़ी स्टैक के साथ काम करने के लिए, उन्हें हमेशा ScrollView के अंदर रखें — स्क्रॉलिंग के बिना, स्क्रीन सीमा से परे जाने वाले तत्व केवल क्लिप हो जाएँगे, लेज़ी रूप से नहीं बनाए जाएँगे।
LazyVStack में लेज़ी लोडिंग तंत्र ज्यामिति पर आधारित है: SwiftUI ScrollView कंटेनर के सापेक्ष प्रत्येक चाइल्ड व्यू की स्थिति को ट्रैक करता है। जब कोई तत्व दृश्य क्षेत्र की सीमा को पार करता है (कुछ बिंदुओं के एक छोटे बफर के साथ), सिस्टम इसके इनिशियलाइज़र को कॉल करता है और सामग्री रेंडर करता है। जब कोई तत्व स्क्रीन छोड़ता है, SwiftUI व्यू को नष्ट कर देता है लेकिन @State के माध्यम से स्थिति को संरक्षित करता है यदि इसे संरक्षणीय के रूप में चिह्नित किया गया है।
यह दृष्टिकोण VStack से भिन्न है, जहाँ सभी चाइल्ड व्यू कंटेनर के इनिशियलाइज़ेशन पर तुरंत बनाए जाते हैं, उनकी दृश्यता की परवाह किए बिना। 10,000 तत्वों की सूची के लिए, VStack मेमोरी में 10,000 व्यू इंस्टेंस बनाएगा, जबकि LazyVStack केवल वे बनाएगा जो स्क्रीन पर फिट होते हैं (आमतौर पर 8–15)।
LazyVStack तीन कॉन्फ़िगरेशन पैरामीटर स्वीकार करता है: alignment (HorizontalAlignment — leading, center, trailing), spacing (CGFloat — तत्वों के बीच स्पेसिंग), और pinnedViews (PinnedScrollableViews — सेक्शन हेडर पिन करना)। LazyHStack समान पैरामीटर का उपयोग करता है, लेकिन alignment VerticalAlignment (top, center, bottom) स्वीकार करता है।
LazyVStack और VStack के बीच मुख्य अंतर चाइल्ड तत्वों को बनाने की रणनीति है। VStack (ईगर स्टैक) रेंडर समय पर सभी चाइल्ड व्यू के आकार और स्थिति की गणना करता है, जो इसे बड़ी डायनामिक सूचियों के लिए अनुपयुक्त बनाता है। LazyVStack (लेज़ी स्टैक) निर्माण को तब तक स्थगित करता है जब तक तत्व दृश्यमान न हो जाए।
आइए 1000 टेक्स्ट लाइनों की सूची के उदाहरण पर व्यवहार की तुलना करें। VStack सभी 1000 लाइनों को तुरंत मेमोरी में लोड करेगा, प्रत्येक लाइन के इनिशियलाइज़र को कॉल करेगा और उसके लिए मेमोरी आवंटित करेगा। इससे कमज़ोर उपकरणों (iPhone SE, iPad mini) पर प्रदर्शन में गिरावट आती है और स्क्रीन स्टार्टअप समय बढ़ जाता है। LazyVStack केवल दृश्यमान 10–12 लाइनें लोड करेगा, बाकी को स्क्रॉल करते समय बनाएगा।
एक व्यावहारिक परीक्षण (Xcode Instruments, Allocations प्रोफ़ाइल का उपयोग करके) दिखाता है: iPhone 12 mini पर, LazyVStack के साथ 5000 तत्वों की सूची 3–5 MB मेमोरी खपत करती है, जबकि समान सामग्री वाला VStack 150–250 MB खपत करता है — 50 गुना अधिक। वहीं, LazyVStack के लिए प्रारंभिक रेंडर समय ~50 ms है जबकि उसी डिवाइस पर VStack के लिए ~800 ms है।
स्थिर या छोटी सूचियों (10–15 तत्वों तक) के लिए VStack चुनें, और किसी भी डायनामिक या संभावित रूप से लंबी सूचियों के लिए LazyVStack चुनें। Apple सूची के अधिकतम आकार के बारे में सुनिश्चित न होने पर डिफ़ॉल्ट रूप से LazyVStack का उपयोग करने की अनुशंसा करता है।
VStack स्थिर इंटरफ़ेस के लिए सबसे अच्छा विकल्प बना हुआ है: प्रोफ़ाइल स्क्रीन, लॉगिन फ़ॉर्म, उत्पाद कार्ड — जहाँ तत्वों की संख्या ज्ञात है और 10–15 से अधिक नहीं है। VStack ऐसी मात्राओं के लिए प्रारंभिक रेंडर पर तेज़ी से काम करता है क्योंकि यह ज्यामिति ट्रैकिंग और लेज़ी लोडिंग पर संसाधन बर्बाद नहीं करता है। इसके अलावा, VStack ScrollView के बाहर सही ढंग से काम करता है (जैसे, ZStack या Group के अंदर), जबकि LazyVStack ScrollView के बिना अपना उद्देश्य खो देता है।
लेज़ी स्टैक बड़ी या अप्रत्याशित संख्या में तत्वों वाले परिदृश्यों के लिए इष्टतम हैं: सोशल मीडिया फ़ीड, उत्पाद कैटलॉग, चैट सूचियाँ, मीडिया फ़ाइल लाइब्रेरी, इवेंट लॉग, हज़ारों रिकॉर्ड वाले एडमिन पैनल।
विशिष्ट उपयोग के मामले: मैसेंजर में संदेश सूची (दसियों हज़ार संदेश), गैलरी ऐप में इमेज कैरोसेल, अनंत लोडिंग वाला न्यूज़ फ़ीड, ऑनलाइन स्टोर में ऑर्डर सूची। LazyHStack विशेष रूप से हॉरिज़ॉन्टल कैरोसेल के लिए उपयोगी है — उदाहरण के लिए, Instagram Stories या प्रमोशनल बैनर।
मतभेद: तत्व प्रकटन एनिमेशन वाले इंटरफ़ेस (लेज़ी स्टैक अतिरिक्त तर्क के बिना तत्व हटाने की स्थितियों के बीच संक्रमण का समर्थन नहीं करते), ऐसे मामले जहाँ सभी तत्व एक साथ दृश्यमान होने चाहिए (चेकबॉक्स की एक छोटी सूची), और जब आपको सेल पुन: उपयोग पर सटीक नियंत्रण की आवश्यकता हो (इस मामले में List या Table बेहतर हो सकते हैं)।
एक बुनियादी उदाहरण न्यूनतम मेमोरी खपत के साथ 1000 तत्व प्रदर्शित करता है। मुख्य तत्व: ScrollView स्क्रॉल कंटेनर के रूप में, LazyVStack लेज़ी लोडिंग के लिए, 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("आइटम #\(index)")
.font(.body)
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
}
}
.padding()
}
}
}
कोड एक ScrollView बनाता है जिसमें तत्वों के बीच 8pt स्पेसिंग के साथ LazyVStack है। ForEach items सरणी पर पुनरावृत्ति करता है और प्रत्येक इंडेक्स के लिए एक Text बनाता है। लेज़ी लोडिंग के लिए धन्यवाद, 1000 तत्वों में से केवल दृश्यमान 10–12 एक बार में मेमोरी में होते हैं।
यह उदाहरण iOS संपर्कों के समान, पिन किए गए हेडर के साथ सेक्शन द्वारा तत्वों के समूहीकरण को दर्शाता है। Section हेडर और सामग्री को परिभाषित करता है, pinnedViews: .sectionHeaders स्क्रॉल करते समय हेडर को स्क्रीन के शीर्ष पर फिक्स करता है।
import SwiftUI
struct SectionedList: View {
let cities = ["मॉस्को", "लंदन", "टोक्यो", "न्यूयॉर्क", "पेरिस"]
let countries = ["रूस", "यूके", "जापान", "यूएसए", "फ्रांस"]
var body: some View {
ScrollView {
LazyVStack(pinnedViews: .sectionHeaders) {
Section(header: Text("शहर").font(.title).bold()) {
ForEach(cities, id: \.self) { city in
Text(city).padding(8)
}
}
Section(header: Text("देश").font(.title).bold()) {
ForEach(countries, id: \.self) { country in
Text(country).padding(8)
}
}
}
}
}
}
पिन किए गए हेडर (.sectionHeaders) UITableView section headers की तरह व्यवहार करते हैं: किसी सेक्शन को स्क्रॉल करते समय, हेडर स्क्रीन के ऊपरी किनारे पर "चिपक" जाता है जब तक कि पूरा सेक्शन गायब न हो जाए, जिसके बाद इसे अगले सेक्शन के हेडर से बदल दिया जाता है। pinnedViews को जोड़ा जा सकता है: .sectionHeaders और .sectionFooters एक साथ।
LazyHStack का उपयोग हॉरिज़ॉन्टल स्क्रॉलिंग के लिए किया जाता है — इमेज कैरोसेल, हॉरिज़ॉन्टल श्रेणी सूचियाँ। alignment: .top पैरामीटर तत्वों को ऊपरी किनारे पर संरेखित करता है।
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)
}
}
कोड LazyHStack के साथ एक हॉरिज़ॉन्टल ScrollView बनाता है। 100 आयतों में से, केवल 2–3 एक साथ प्रदर्शित होते हैं (स्क्रीन चौड़ाई और तत्व आकार पर निर्भर करता है)। बाएँ स्क्रॉल करने पर, नए तत्व लेज़ी रूप से लोड होते हैं। कंटेनर की ऊँचाई निश्चित (220pt) है ताकि हॉरिज़ॉन्टल स्क्रॉलिंग में अनंत ऊँचाई से बचा जा सके।
PinnedScrollableViews LazyVStack और LazyHStack के लिए एक कॉन्फ़िगरेशन विकल्प है जो स्क्रॉल करते समय सेक्शन हेडर और फ़ुटर को पिन करने को नियंत्रित करता है। दो मान समर्थित हैं: sectionHeaders (हेडर कंटेनर की शुरुआत में चिपकते हैं) और sectionFooters (फ़ुटर अंत में चिपकते हैं)।
पिन किए गए व्यू तंत्र केवल LazyVStack में नेस्टेड Section कंटेनर के अंदर काम करता है। प्रत्येक Section में एक हेडर और/या फ़ुटर होता है जो स्वचालित रूप से चिपकने वाला व्यवहार प्राप्त करता है। SwiftUI ScrollView सीमाओं के सापेक्ष प्रत्येक सेक्शन की स्थिति को ट्रैक करता है और सेक्शन के बीच संक्रमण करते समय पिन किए गए तत्व की दृश्यता को स्विच करता है।
महत्वपूर्ण: pinnedViews लेआउट गणना जटिलता को बढ़ाते हैं क्योंकि SwiftUI को लगातार पुनर्गणना करनी पड़ती है कि वर्तमान में कौन सा हेडर पिन है। केवल तब pinnedViews का उपयोग करें जब कार्यक्षमता वास्तव में आवश्यक हो — सेक्शन के बिना सरल सूचियों के लिए, इस पैरामीटर को छोड़ना बेहतर है। Apple अपने दस्तावेज़ (Human Interface Guidelines, 2024) में वर्णमाला सूचकांक और दिनांक-आधारित समूहीकरण के लिए पिन किए गए हेडर का उपयोग करने की अनुशंसा करता है।
पहचानकर्ताओं का सही उपयोग LazyVStack के लिए सबसे महत्वपूर्ण प्रदर्शन कारक है। ForEach में प्रत्येक तत्व का एक स्थिर अद्वितीय id होना चाहिए। प्रिमिटिव (Int, String) के साथ \.self का उपयोग स्वीकार्य है, लेकिन डेटा मॉडल के लिए हमेशा Identifiable प्रोटोकॉल लागू करें। अस्थिर id (जैसे, हर बार उत्पन्न UUID) SwiftUI को हर अपडेट पर सभी व्यू को पुनः बनाने के लिए मजबूर करती हैं।
प्रत्येक स्टैक तत्व के body के अंदर भारी गणनाओं से बचें। यदि किसी तत्व में जटिल लेआउट या डेटा प्रोसेसिंग है — तो तर्क को अपनी लेज़ी लोडिंग के साथ एक अलग व्यू संरचना में निकालें। अनावश्यक पुन: रेंडर को रोकने के लिए EquatableView का उपयोग करें जब तत्व का डेटा नहीं बदला है।
LazyVStack के अंदर छवियों के लिए, हमेशा एसिंक्रोनस लोडिंग (AsyncImage) या Kingfisher/Nuke के माध्यम से कैशिंग का उपयोग करें। प्रत्येक तत्व को स्क्रीन पर दिखाई देने पर सिंक्रोनस रूप से इमेज लोड नहीं करनी चाहिए — इससे स्क्रॉल जैंक होगा। WWDC Session 10031 के अनुसार, प्रीफ़ेच बफर का इष्टतम आकार वर्तमान स्थिति से 3–5 स्क्रीन आगे और पीछे है।
SwiftUI प्रोफ़ाइल के साथ Xcode Instruments का उपयोग करके प्रदर्शन मापें। मीट्रिक पर ध्यान दें: body evaluations, allocations, और फ्रेम दर (FPS)। लक्ष्य मान: स्क्रॉल करते समय FPS > 55, प्रति तत्व रेंडर समय < 1 ms।
अक्सर पूछे जाने वाले प्रश्न
List अंतर्निहित क्षमताएँ प्रदान करता है: स्वाइप-टू-एडिट (swipeActions), .onDelete के माध्यम से हटाना, .onMove के माध्यम से पुनर्क्रमण, .insetGrouped शैली। LazyVStack एक निचले स्तर का उपकरण है जिसमें एडिटिंग जेस्चर के लिए अंतर्निहित समर्थन नहीं है। List आंतरिक रूप से LazyVStack का उपयोग करता है लेकिन मूल iOS तालिका शैली जोड़ता है। यदि आपको कस्टम सेल डिज़ाइन चाहिए और अंतर्निहित एडिटिंग की आवश्यकता नहीं है — LazyVStack चुनें। यदि आपको swipeActions, .onDelete और @FetchRequest के साथ काम करने की आवश्यकता है — List का उपयोग करें।
लेज़ी स्टैक प्रीफ़ेचिंग का उपयोग करते हैं — SwiftUI स्मूथ स्क्रॉलिंग सुनिश्चित करने के लिए एक छोटे अग्रिम बफर (प्रीफ़ेच बफर) के साथ तत्व बनाता है। बफर का आकार स्वचालित रूप से स्क्रॉलिंग गति और डिवाइस प्रदर्शन के अनुसार समायोजित होता है। Apple प्रोफ़ाइलिंग डेटा के अनुसार, प्रीफ़ेच बफर आमतौर पर स्क्रॉल दिशा में 1–3 स्क्रीन होता है। यदि आप देखते हैं कि बहुत अधिक अदृश्य तत्व बनाए जा रहे हैं, तो जाँच करें कि क्या आपके पास हर बार उत्पन्न होने वाले पहचानकर्ता हैं या व्यू इनिशियलाइज़र में भारी गणनाएँ हैं।
हाँ, लेकिन सीमाओं के साथ। LazyVStack को VStack के अंदर नेस्ट करना व्यर्थ है — बाहरी VStack आंतरिक LazyVStack के सभी तत्वों को तुरंत बनाएगा, लेज़ी लोडिंग को रद्द कर देगा। VStack को LazyVStack के अंदर नेस्ट करना ठीक है और लेज़ी तंत्र को नहीं तोड़ता है। LazyVStack को दूसरे LazyVStack के अंदर नेस्ट करना नेस्टेड सेक्शन के लिए स्वीकार्य है, लेकिन प्रदर्शन पर ध्यान दें: प्रत्येक स्तर ज्यामिति ट्रैकिंग के लिए ओवरहेड जोड़ता है।
SwiftUI LazyVStack के लिए अंतर्निहित विभाजक प्रदान नहीं करता है। उन्हें मैन्युअल रूप से जोड़ें: ForEach में प्रत्येक तत्व के बाद Divider() रखें, या प्रत्येक तत्व पर .overlay(Divider(), alignment: .bottom) मॉडिफ़ायर का उपयोग करें। कस्टम विभाजक के लिए, Rectangle().frame(height: 1).foregroundColor(.gray.opacity(0.3)) बनाएँ।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।