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 অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।