SwiftUI میں LazyVStack اور LazyHStack کے بارے میں جانیں — iOS، macOS، watchOS اور tvOS پر اسکرول کرنے والی فہرستوں، گرڈز اور کیروسلز کو موثر طریقے سے رینڈر کرنے والے سست اسٹیک۔ عام VStack اور HStack کے برعکس، سست اسٹیک عناصر کو صرف اس وقت بناتے ہیں جب وہ دکھائی دینے والے علاقے میں ظاہر ہوتے ہیں، جو بڑے ڈیٹا سیٹ کے ساتھ کام کرتے وقت میموری کی کھپت کو نمایاں طور پر کم کرتا ہے۔ سست اسٹیک کا فن تعمیر Layout پروٹوکول پر مبنی ہے اور ForEach اور ScrollView کے ذریعے شناخت کے ساتھ مربوط ہے۔
اہم نکات
LazyVStack اور LazyHStack SwiftUI میں لے آؤٹ کنٹینر ہیں جو چائلڈ ویوز کو صرف ضرورت پڑنے پر بناتے اور دکھاتے ہیں، جب وہ اسکرول ہونے والے علاقے میں دکھائی دیتے ہیں۔ LazyVStack عناصر کو عمودی طور پر (اوپر سے نیچے) ترتیب دیتا ہے، جبکہ LazyHStack انہیں افقی طور پر (بائیں سے دائیں) ترتیب دیتا ہے۔
دونوں اسٹیک ایپل نے 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 سیکشن ہیڈرز کی طرح برتاؤ کرتے ہیں: کسی سیکشن کو اسکرول کرتے وقت، ہیڈر اسکرین کے اوپری کنارے پر "چپک" جاتا ہے جب تک کہ پورا سیکشن غائب نہ ہو جائے، جس کے بعد اسے اگلے سیکشن کے ہیڈر سے بدل دیا جاتا ہے۔ 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 اسکرین آگے اور پیچھے ہے۔
Xcode Instruments کو SwiftUI پروفائل کے ساتھ استعمال کرکے کارکردگی کی پیمائش کریں۔ میٹرکس پر توجہ دیں: 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 ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔