NavigationStack هو حاوية تنقل حديثة في SwiftUI، تم تقديمها في iOS 16+ وحلت محل NavigationView. وفقًا لوثائق مطوري Apple، 2024، يدير NavigationStack مكدس الشاشات عبر مسار تنقل آمن النوع (NavigationPath)، ويدعم التنقل العميق، والعودة البرمجية إلى الشاشة الجذرية، والحفاظ على الحالة عند تغيير البيانات. على عكس NavigationView، لا يتطلب NavigationStack التغليف في حاوية إضافية ويوفر Binding مباشرًا لمسار التنقل.
أهم النقاط
NavigationStack هو عرض حاوية ينفذ التنقل القائم على المكدس (LIFO). يدير سجل الانتقالات، مما يسمح بوضع شاشات جديدة على المكدس والعودة للخلف عبر زر “الرجوع” في النظام أو برمجيًا. NavigationStack جزء من SwiftUI بدءًا من iOS 16 وiPadOS 16 وmacOS 13 وwatchOS 9 وtvOS 16.
الابتكار الرئيسي في NavigationStack هو مسار التنقل الآمن النوع. بدلاً من تحديد وجهة مباشرة عند إنشاء NavigationLink، تضع قيمة في المسار، ويتم تسجيل عرض الوجهة بشكل منفصل عبر المعدل .navigationDestination(for:destination:). هذا يفصل التنقل عن العرض، مما يجعل الكود أكثر نمطية وقابلية للاختبار.
وفقًا WWDC 2022 (الجلسة 10054)، يستخدم NavigationStack آلية تنقل جديدة تعتمد على ObservableObject ودورة حياة SwiftUI. على عكس NavigationView الذي اعتمد على UINavigationController داخليًا، تم تنفيذ NavigationStack بالكامل في SwiftUI، مما يحسن القدرة على التنبؤ والتوافق مع دورة حياة SwiftUI.
يقبل NavigationStack عرضًا جذريًا ومسار تنقل اختياريًا (Binding إلى NavigationPath أو مصفوفة من قيم Hashable). يتم وضع جميع الشاشات الفرعية في المكدس عبر NavigationLink أو بإضافة قيم برمجيًا إلى المسار.
NavigationView كان حاوية التنقل الرئيسية في SwiftUI قبل iOS 16. كان يدير UINavigationController تلقائيًا داخليًا، مما أدى إلى عدة مشاكل: سلوك غير متوقع عند تغيير البيانات، تعقيد في التنقل البرمجي، وعدم وجود أمان النوع.
| الخاصية | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| نوع التنقل | مكدس (LIFO) | مكدس (LIFO) |
| مسار التنقل | محدد النوع (NavigationPath) | غير مدعوم |
| Deep linking | دعم مدمج | يتطلب حلاً |
| العودة البرمجية | عبر المسار (pop، popToRoot) | dismiss، presentationMode |
| داخليًا | SwiftUI أصلي | UINavigationController |
| التوافق | iOS 16+ | iOS 13+ |
الميزة الرئيسية لـ NavigationStack — التنقل الآمن النوع. تحدد المسار كمصفوفة من أنواع محددة (أو NavigationPath للمكدسات غير المتجانسة) وتسجل وجهة لكل نوع. هذا يلغي أخطاء عدم تطابق الأنواع ويجعل التنقل قابلًا للتنبؤ.
NavigationView مهمل في iOS 17. توصي Apple بالترحيل إلى NavigationStack لجميع المشاريع الجديدة وعند تحديث الإصدار الأدنى إلى iOS 16.
NavigationPath هو نوع يمثل مسار التنقل في NavigationStack. يمكنه تخزين قيم غير متجانسة (AnyHashable) أو استخدامه مع نوع محدد عبر Binding إلى مصفوفة [T: Hashable]. يتم ترميز NavigationPath وفك ترميزه تلقائيًا للحفاظ على الحالة.
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: String.self) { value in
DetailView(id: value)
}
.navigationDestination(for: Int.self) { value in
NumberView(number: value)
}
}
}
func goToRoot() {
path.removeLast(path.count)
}
func pushDeepLink() {
path.append("detail_42")
}
}
مكدس غير متجانس: يمكن لـ NavigationPath احتواء قيم من أنواع مختلفة إذا كانت تطبق Hashable. على سبيل المثال، قد تقبل الشاشة الأولى String (معرف)، والثانية Int (رقم)، والثالثة enum Route مخصص. يسجل كل نوع .navigationDestination منفصل للعرض.
دعم Codable: يطبق NavigationPath Codable إذا كانت جميع القيم في المسار هي أيضًا Codable + Hashable. يتيح ذلك حفظ واستعادة حالة التنقل عند إعادة تشغيل التطبيق أو عند الدخول في الخلفية.
.navigationDestination(for:destination:) — معدل يسجل عرض وجهة لنوع بيانات محدد. عندما يضع NavigationLink قيمة من هذا النوع في المسار، يجد SwiftUI تلقائيًا .navigationDestination المقابل وينشئ الشاشة.
enum AppRoute: Hashable {
case profile(UserID)
case settings
case about
}
struct AppNavigation: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile(let id):
ProfileView(userId: id)
case .settings:
SettingsView()
case .about:
AboutView()
}
}
}
}
}
// Navigate: path.append(AppRoute.profile("user_123"))
قاعدة مهمة: يجب تطبيق .navigationDestination على عرض موجود داخل NavigationStack، وقبل أن يضع NavigationLink قيمة في المسار. عادةً ما يُضاف إلى العرض الجذري أو حاوية قسم. إذا لم يتم العثور على .navigationDestination لنوع القيمة، لن يحدث الانتقال.
وفقًا SwiftUI Engineering (2023)، يمكن تسجيل .navigationDestination على مستويات مختلفة من التسلسل الهرمي. يبحث SwiftUI عن أقرب وجهة مناسبة أثناء التنقل. يتيح ذلك تجاوز الوجهة لنوع واحد في أجزاء مختلفة من التطبيق.
النمط 1: التنقل عبر enum Route. حدد enum بقيم مرتبطة لجميع شاشات التطبيق. استخدم .navigationDestination واحدًا لـ AppRoute و switch للتوجيه. يوفر هذا مصدرًا واحدًا للحقيقة لجميع الانتقالات الممكنة في التطبيق.
struct StoreView: View {
@State private var path: [ProductRoute] = []
var body: some View {
NavigationStack(path: $path) {
ProductGrid()
.navigationDestination(for: ProductRoute.self) { route in
switch route {
case .detail(let product):
ProductDetail(product: product)
case .reviews(let productId):
ReviewsView(productId: productId)
}
}
}
}
}
enum ProductRoute: Hashable {
case detail(Product)
case reviews(String)
}
النمط 2: التنقل البرمجي و deep linking. يتيح NavigationStack إدارة المكدس برمجيًا: إضافة وإزالة الشاشات والعودة إلى الجذر. هذا ضروري للإشعارات الفورية والروابط العميقة واستعادة التنقل بعد إعادة التشغيل.
النمط 3: مصفوفة من الأنواع بدلاً من NavigationPath. إذا كانت جميع الشاشات تستخدم نفس النوع (مثل String أو enum مخصص)، استخدم Binding إلى [T]. يوفر هذا كتابة أكثر صرامة وأداء أفضل من NavigationPath. NavigationPath مبرر للمكدسات غير المتجانسة بأنواع شاشات مختلفة.
وفقًا Point-Free (2024)، فإن NavigationStack مع enum Route هو الطريقة المفضلة لتنظيم التنقل في تطبيقات SwiftUI. يجعل جميع الانتقالات الممكنة صريحة وآمنة النوع وقابلة للاختبار، وهو أمر مهم بشكل خاص للمشاريع الكبيرة التي تحتوي على عشرات الشاشات.
الأسئلة الشائعة
NavigationStack — حاوية تنقل SwiftUI (iOS 16+) تدير مكدس شاشات عبر مسار آمن النوع. حل محل NavigationView، ويقدم دعمًا للروابط العميقة والتنقل البرمجي والحفاظ على الحالة.
يستخدم NavigationStack مسارًا آمن النوع (NavigationPath) بدلاً من ربط NavigationLink مباشرة بالوجهة. يدعم التنقل البرمجي والروابط العميقة و Codable للحفاظ على الحالة. يعمل على SwiftUI، وليس عبر UINavigationController.
NavigationPath هو نوع يمثل تسلسل الشاشات في المكدس. تضيف قيمًا إلى المسار عبر path.append() أو NavigationLink مع value. يسجل كل نوع .navigationDestination للعرض. يدعم NavigationPath Codable والحفظ التلقائي للحالة.
عبر الإدارة البرمجية للمسار: بعد معالجة URL، استدعِ path.append() مع قيمة المسار المقابلة. يعرض NavigationStack تلقائيًا الشاشة المستهدفة. العودة إلى الجذر — path.removeLast(path.count).
نعم، إذا كان إصدارك الأدنى iOS 16+. NavigationView مهمل في iOS 17. يوفر الترحيل تنقلًا آمن النوع ودعمًا للروابط العميقة وتوافقًا أفضل مع دورة حياة SwiftUI. للمشاريع التي تستهدف iOS 15 وما دونه، استمر في استخدام NavigationView حاليًا.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.