List — یک View کانتینری در SwiftUI برای نمایش دادهها به صورت یک فهرست عمودی قابل پیمایش، مشابه UITableView در UIKit است. بر اساس Apple Developer Documentation, 2024، List از بخشهای ایستا و پویا، عملیات کشیدن، جابجایی ردیفها و pull-to-refresh پشتیبانی میکند. بر خلاف UITableView، List از API اعلامی مبتنی بر SwiftUI و ForEach استفاده میکند و به طور خودکار استفاده مجدد از سلولها و عملکرد را در تعداد زیاد ردیفها مدیریت مینماید.
نکات کلیدی
List — یک View است که دنبالهای از عناصر را در یک فهرست عمودی قابل پیمایش نمایش میدهد. این View همراه با iOS 13 و SwiftUI معرفی شد و روش اصلی نمایش فهرستهای دادهها است که جایگزین UITableView در UIKit میشود. List به طور خودکار استفاده مجدد از سلولها، پیمایش و عملکرد را مدیریت میکند.
List از lazy-loading استفاده میکند: سلولها در حین پیمایش ایجاد میشوند، نه یکباره. این آن را از VStack با ForEach درون ScrollView متمایز میکند، جایی که همه سلولها هنگام رندر ایجاد میشوند. List همچنین پشتیبانی داخلی از عملیات کشیدن، pull-to-refresh، ویرایش (حذف/انتقال) و انتخاب ردیفها را فراهم میکند.
بر اساس Apple WWDC 2021 (Session 10072)، List در iOS 15+ به لطف مکانیزم diffing جدید در سطح مجموعهها، بهبودهای عملکردی قابل توجهی دریافت کرد. این کار List را در بهروزرسانی دادهها، به ویژه برای فهرستهای با صدها ردیف، کارآمدتر ساخت.
توسعهدهندگان اغلب بین List و ScrollView با VStack برای نمایش مجموعهای از Viewها انتخاب میکنند. تفاوت کلیدی: List از استفاده مجدد سلولها (مانند UITableView) استفاده میکند، در حالی که ScrollView + VStack همه Viewها را یکباره ایجاد میکند. برای فهرستهای با تعداد ثابت عناصر (تا ۲۰) تفاوت ناچیز است. برای فهرستهای پویا با ۵۰+ ردیف، List از نظر عملکرد ترجیح داده میشود.
فهرست ایستا (Static List) — فهرستی با تعداد ثابت ردیفها است که مستقیماً در بدنه List تعریف میشوند. برای منوها، تنظیمات و فرمهایی با مجموعه عناصر مشخص استفاده میشود. هر ردیف به صراحت و بدون حلقه یا ForEach اعلام میشود.
// فهرست ایستا (برای منوها و تنظیمات)
List {
Text("پروفایل")
Text("تنظیمات")
Text("درباره")
}
// فهرست پویا (برای دادهها)
struct UserList: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
Text(user.name)
Text(user.role)
.foregroundColor(.secondary)
}
}
}
}
فهرست پویا (Dynamic List) از مقداردهنده List(data:rowContent:) یا ForEach درون بدنه List استفاده میکند. گزینه اول زمانی مناسب است که هر ردیف با یک عنصر داده مطابقت دارد. گزینه دوم — زمانی که بخشها یا عناصر اضافی بین دادهها وجود دارد.
شناسایی (Identifiable): برای فهرستهای پویا، عناصر داده باید با پروتکل Identifiable مطابقت داشته باشند یا در تاپل data:id باید KeyPath به شناسه یکتا مشخص شود. SwiftUI از شناسهها برای ردیابی تغییرات استفاده میکند: افزودن، حذف و جابجایی ردیفها.
Section — یک View برای گروهبندی ردیفها در List با عنوان و پاورقی اختیاری است. Section header و footer را به عنوان ViewBuilder میپذیرد و امکان استفاده از نه تنها متن، بلکه Viewهای سفارشی برای عناوین بخشها را فراهم میکند.
struct SettingsView: View {
var body: some View {
List {
Section(header: Text("حساب")) {
Text("نام")
Text("ایمیل")
}
Section(header: Text("اعلانها")) {
Toggle("Push", isOn: $pushEnabled)
Toggle("Email", isOn: $emailEnabled)
}
}
.listStyle(.insetGrouped)
}
}
// بخشهای پویا با ForEach
List {
ForEach(groupedData.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(groupedData[key]!) { item in
Text(item.title)
}
}
}
}
سبکهای List: SwiftUI چندین سبک داخلی را از طریق اصلاحکننده .listStyle() ارائه میدهد. .insetGrouped — استاندارد برای iOS Settings، .plain — مینیمال، .inset — با فاصله، .sidebar — برای Sidebar در iPad.
بر اساس SwiftUI Cookbook (2024)، Section با بخشهای پویا و ForEach درون آن، الگوی استانداردی برای گروهبندی دادهها در برنامههای با ساختار پیچیده است. قانون کلیدی: Section را درون Section قرار ندهید و از مقداردهنده List(data:) همراه با Section استفاده نکنید — از ForEach درون بدنه List استفاده کنید.
.swipeActions(edge:allowsFullSwipe:content:) — اصلاحکننده برای iOS 15+ که عملیات کشیدن را به ردیفهای List اضافه میکند. امکان نمایش دکمهها هنگام کشیدن به چپ (پیشفرض) یا راست، با رنگها و نقشهای مختلف (destructive, cancel) را فراهم میکند.
struct TaskList: View {
@Binding var tasks: [Task]
var body: some View {
List {
ForEach($tasks) { $task in
Text(task.title)
.swipeActions(edge: .trailing) {
Button("حذف", role: .destructive) {
tasks.removeAll { $0.id == task.id }
}
}
.swipeActions(edge: .leading) {
Button(task.isDone ? "برگردان" : "انجام شد") {
task.isDone.toggle()
}
.tint(.green)
}
}
}
.refreshable {
// بارگذاری ناهمزمان دادهها
await loadTasks()
}
}
}
.refreshable — اصلاحکننده برای iOS 15+ که pull-to-refresh را اضافه میکند. یک closure ناهمزمان (async) میپذیرد که وقتی کاربر فهرست را به پایین میکشد اجرا میشود. SwiftUI به طور خودکار نشانگر بارگذاری را نمایش میدهد. پس از اتمام عملیات، نشانگر پنهان میشود.
.onDelete و .onMove — اصلاحکنندههایی برای iOS 13+ که پشتیبانی از حذف و جابجایی ردیفها را اضافه میکنند. برای استفاده از آنها، دادهها را در ForEach با Binding بپیچید یا closureها را از طریق .onDelete(perform:) روی List یا ForEach ارسال کنید.
عملکرد List به تعداد ردیفها، پیچیدگی هر سلول و فراوانی بهروزرسانی دادهها بستگی دارد. SwiftUI از lazy-loading و استفاده مجدد از سلولها (مشابه UITableView.dequeueReusableCell) استفاده میکند، اما بهینهسازیهای اضافی ممکن است برای فهرستهای با ۵۰۰+ ردیف لازم باشد.
| بهینهسازی | توضیح | نسخه iOS |
|---|---|---|
| Identifiable | شناسه یکتا برای هر عنصر | iOS 13+ |
| EquatableView | جلوگیری از رندر مجدد در دادههای برابر | iOS 13+ |
| id(_:) | بازآفرینی اجباری View هنگام تغییر ID | iOS 13+ |
| .equatable() | مقایسه دقیق بر اساس Equatable | iOS 15+ |
| Diffable data | diff خودکار در تغییرات | iOS 15+ |
مشکل ۱: بهروزرسانیهای مکرر. اگر دادههای فهرست مکرراً بهروزرسانی شوند (مثلاً هر ثانیه)، List ممکن است سلولهای قابل مشاهده را در هر تغییر وضعیت دوباره رندر کند. راهحل: از struct (value types) برای دادهها استفاده کنید — SwiftUI آنها را بر اساس مقدار مقایسه میکند و فقط ردیفهای تغییر یافته را دوباره رندر میکند.
مشکل ۲: سلولهای سنگین. اگر هر ردیف شامل سلسلهمراتب پیچیده View، تصاویر و انیمیشنها باشد، پیمایش ممکن است کند شود. راهحل: سلولها را به Viewهای جداگانه منتقل کنید، از EquatableView برای جلوگیری از رندر مجدد اضافی استفاده کنید. بر اساس SwiftUI Lab (2024)، تقسیم یک ردیف پیچیده به زیر مؤلفهها زمان رندر را ۳۰–۵۰٪ کاهش میدهد.
مشکل ۳: تعداد زیاد ردیفها. در ۱۰۰۰+ ردیف، List به لطف lazy-loading همچنان کارآمد کار میکند، اما بارگذاری اولیه ممکن است به دلیل محاسبه layout کند شود. راهحل: از LazyVStack فقط برای فهرستهای با ردیفهای همگن که به قابلیتهای List (کشیدن، بخشها) نیاز ندارند استفاده کنید. برای فهرستهای کاملاً کاربردی، List بهترین گزینه باقی میماند.
سؤالات متداول
List — یک View کانتینری برای نمایش فهرست قابل پیمایش دادهها در SwiftUI. مشابه UITableView در UIKit با API اعلامی. از بخشها، عملیات کشیدن، pull-to-refresh، ویرایش و سفارشیسازی از طریق .listStyle() پشتیبانی میکند.
List از lazy-loading و استفاده مجدد از سلولها استفاده میکند — سلولها در حین پیمایش ایجاد میشوند. ScrollView + VStack همه Viewها را یکباره ایجاد میکند. برای فهرستهای با ۵۰+ ردیف، List ترجیح داده میشود. برای مجموعههای ثابت کوچک (تا ۲۰ عنصر) تفاوت ناچیز است.
از اصلاحکننده .refreshable (iOS 15+) استفاده کنید. یک closure ناهمزمان با منطق بهروزرسانی دادهها ارسال کنید. SwiftUI به طور خودکار نشانگر بارگذاری را نمایش میدهد و پس از اتمام عملیات ناهمزمان آن را پنهان میکند.
از Section View با header و پاورقی اختیاری استفاده کنید. ردیفهای فهرست را درون Section قرار دهید. برای بخشهای پویا از ForEach با groupedData استفاده کنید. سبک فهرست از طریق .listStyle(.insetGrouped) برای ظاهری شبیه iOS تنظیم میشود.
برای دادهها از struct (value types) استفاده کنید، سلولهای پیچیده را به Viewهای جداگانه با EquatableView منتقل کنید، از بهروزرسانی مکرر وضعیت در هر ردیف خودداری کنید. برای فهرستهای با ۱۰۰۰+ ردیف، اگر به قابلیتهای List نیاز ندارید، LazyVStack را در نظر بگیرید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید