List هي View حاوية في SwiftUI لعرض البيانات كقائمة قابلة للتمرير عمودياً، مماثلة لـ UITableView في UIKit. وفقاً لـ Apple Developer Documentation, 2024، تدعم List الأقسام الثابتة والديناميكية، إجراءات السحب، إعادة ترتيب الصفوف، وسحب التحديث. على عكس UITableView، تستخدم List واجهة برمجة تطبيقات تصريحية مبنية على SwiftUI و ForEach، وتدير تلقائياً إعادة استخدام الخلايا والأداء مع عدد كبير من الصفوف.
الخلاصة
List هي View تعرض سلسلة من العناصر في قائمة قابلة للتمرير عمودياً. تم تقديمها في iOS 13 مع SwiftUI وهي الطريقة الأساسية لعرض قوائم البيانات، لتحل محل UITableView من UIKit. تدير List تلقائياً إعادة استخدام الخلايا والتمرير والأداء.
تستخدم List التحميل البطيء: يتم إنشاء الخلايا أثناء التمرير، وليس كلها مرة واحدة. وهذا ما يميزها عن VStack مع ForEach داخل ScrollView، حيث يتم إنشاء جميع الخلايا وقت العرض. توفر List أيضاً دعماً مدمجاً لإجراءات السحب وسحب التحديث والتحرير (حذف/نقل) واختيار الصفوف.
وفقاً لـ Apple WWDC 2021 (Session 10072)، حصلت List في iOS 15+ على تحسينات كبيرة في الأداء بفضل آلية diffing جديدة على مستوى المجموعات. مما جعل List أكثر كفاءة عند تحديث البيانات، خاصة للقوائم التي تحتوي على مئات الصفوف.
غالباً ما يختار المطورون بين List و ScrollView مع VStack لعرض مجموعة من Views. الفرق الرئيسي: تستخدم List إعادة استخدام الخلايا (مثل UITableView)، بينما يقوم ScrollView + VStack بإنشاء جميع Views فوراً. للقوائم ذات الحجم الثابت (حتى 20 عنصراً)، الفرق ضئيل. للقوائم الديناميكية التي تحتوي على 50+ صفاً، يفضل استخدام 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)
}
}
}
}
القائمة الديناميكية تستخدم المُهيئ List(data:rowContent:) أو ForEach داخل جسم List. الطريقة الأولى مناسبة عندما يتوافق كل صف مع عنصر بيانات واحد. الطريقة الثانية مفيدة عندما تكون هناك أقسام أو عناصر إضافية بين البيانات.
التعريف (Identifiable): للقوائم الديناميكية، يجب أن تتوافق عناصر البيانات مع بروتوكول Identifiable، أو يجب تحديد KeyPath لمعرف فريد في tuple data:id. يستخدم SwiftUI المعرفات لتتبع التغييرات: الإضافات والحذف ونقل الصفوف.
Section هي View لتجميع الصفوف في List مع عنوان و footer اختياري. تقبل Section الرأس و footer كـ ViewBuilder، مما يسمح باستخدام ليس فقط النص ولكن أيضاً Views مخصصة لعناوين الأقسام.
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، .plain — بسيط، .inset — مع مسافة بادئة، .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+ يضيف سحب التحديث. يقبل إغلاقاً غير متزامن يتم تنفيذه عندما يسحب المستخدم القائمة لأسفل. يعرض SwiftUI تلقائياً مؤشر التحميل. بعد اكتمال العملية، يتم إخفاء المؤشر.
.onDelete و .onMove — مُعدِّلات لنظام iOS 13+ تضيف دعماً لحذف الصفوف وإعادة ترتيبها. لاستخدامها، قم بتغليف البيانات في ForEach مع Binding أو مرر الإغلاقات عبر .onDelete(perform:) على List أو ForEach.
أداء List يعتمد على عدد الصفوف، تعقيد كل خلية، وتكرار تحديثات البيانات. يستخدم SwiftUI التحميل البطيء وإعادة استخدام الخلايا (مشابهاً لـ UITableView.dequeueReusableCell)، ولكن قد تكون هناك حاجة إلى تحسينات إضافية للقوائم التي تحتوي على 500+ صف.
| التحسين | الوصف | إصدار iOS |
|---|---|---|
| Identifiable | معرفات فريدة لكل عنصر | iOS 13+ |
| EquatableView | تجنب إعادة الرسم عند تساوي البيانات | iOS 13+ |
| id(_:) | إجبار إعادة إنشاء View عند تغيير المعرف | iOS 13+ |
| .equatable() | مقارنة صارمة عبر Equatable | iOS 15+ |
| Diffable data | diff تلقائي عند التغييرات | iOS 15+ |
المشكلة 1: التحديثات المتكررة. إذا تم تحديث البيانات في القائمة بشكل متكرر (على سبيل المثال، كل ثانية)، فقد يعيد List رسم الخلايا المرئية عند كل تغيير حالة. الحل: استخدم الهياكل (value types) للبيانات — يقارنها SwiftUI حسب القيمة ويعيد رسم الصفوف المتغيرة فقط.
المشكلة 2: الخلايا الثقيلة. إذا كان كل صف يحتوي على تسلسل هرمي معقد من Views وصور ورسوم متحركة، فقد يبطئ التمرير. الحل: استخرج الخلايا إلى Views منفصلة، استخدم EquatableView لمنع إعادة الرسم غير الضرورية. وفقاً لـ SwiftUI Lab (2024)، فإن تقسيم الصف المعقد إلى مكونات فرعية يقلل وقت العرض بنسبة 30–50%.
المشكلة 3: عدد كبير من الصفوف. مع 1000+ صف، لا تزال List تعمل بكفاءة بفضل التحميل البطيء، ولكن التحميل الأولي قد يبطئ بسبب حساب التخطيط. الحل: استخدم LazyVStack فقط للقوائم ذات الصفوف المتجانسة حيث لا تكون ميزات List (السحب، الأقسام) مطلوبة. للقوائم كاملة الميزات، تظل List الخيار الأفضل.
الأسئلة الشائعة
List هي View حاوية لعرض قائمة بيانات قابلة للتمرير في SwiftUI. وهي مكافئة لـ UITableView في UIKit بواجهة برمجة تطبيقات تصريحية. تدعم الأقسام وإجراءات السحب وسحب التحديث والتحرير والتخصيص عبر .listStyle().
تستخدم List التحميل البطيء وإعادة استخدام الخلايا — يتم إنشاء الخلايا أثناء التمرير. يقوم ScrollView + VStack بإنشاء جميع Views مرة واحدة. للقوائم التي تحتوي على 50+ صفاً، يفضل استخدام List. للمجموعات الثابتة الصغيرة (حتى 20 عنصراً)، الفرق ضئيل.
استخدم المُعدِّل .refreshable (iOS 15+). مرر إغلاقاً غير متزامن مع منطق تحديث البيانات. يعرض SwiftUI تلقائياً مؤشر التحميل ويخفيه بعد اكتمال العملية غير المتزامنة.
استخدم Section View مع عنوان و footer اختياري. ضع صفوف القائمة داخل Section. للأقسام الديناميكية، استخدم ForEach مع groupedData. يتم تكوين نمط القائمة عبر .listStyle(.insetGrouped) لمظهر شبيه بـ iOS.
استخدم الهياكل (value types) للبيانات، استخرج الخلايا المعقدة إلى Views منفصلة مع EquatableView، تجنب تحديثات الحالة المتكررة في كل صف. للقوائم التي تحتوي على 1000+ صف، فكر في LazyVStack إذا لم تكن ميزات List مطلوبة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.