Push Presentation هو نمط تنقل في التطبيقات المحمولة حيث تُضاف كل شاشة جديدة إلى مكدس التنقل فوق الشاشة السابقة. يمكن للمستخدم التقدم بالتسلسل عبر المحتوى والعودة باستخدام زر النظام أو السحب. وفقاً لـ Apple Developer, 2025، يُستخدم UINavigationController في 85% من تطبيقات iOS كنمط التنقل الرئيسي. في Android، تُطبق وظائف مماثلة عبر FragmentManager و NavController من Navigation Component.
النقاط الرئيسية
Push Presentation هو آلية تنقل يتم فيها وضع كل شاشة جديدة على قمة المكدس، بينما تبقى الشاشة السابقة في الذاكرة تحتها. يتعمق المستخدم في المحتوى عن طريق فتح الشاشات التفصيلية بالتسلسل، ويعيده زر العودة إلى الخطوة السابقة.
تعتمد بنية التنقل Push على بنية بيانات LIFO (Last In, First Out). تُضاف الشاشة الجديدة دائماً إلى نهاية المكدس، ويحدث الحذف فقط من القمة. هذا يضمن سلوكاً متوقعاً: يعرف المستخدم دائماً أن زر العودة سيعيده خطوة واحدة بالضبط إلى الوراء. مكدس التنقل يمكن أن يحتوي على 2 إلى 10+ شاشات حسب تعقيد التطبيق.
Push Presentation هو النمط الرئيسي في التطبيقات ذات المحتوى الهرمي: خلاصات الأخبار، كتالوجات المنتجات، القوائم متعددة المستويات والتوثيق. وفقاً لـ Material Design، التنقل المكدسي مناسب للسيناريوهات حيث يستكشف المستخدم المحتوى من العام إلى الخاص، وكل شاشة تالية تعمق فهم الموضوع.
مكدس التنقل هو مجموعة مرتبة من الشاشات حيث يخزن كل عنصر حالة ViewController أو Fragment الخاص به. أثناء عملية Push، تُضاف شاشة جديدة إلى المكدس، وتتحرك حركتها (انزلاق من اليمين في iOS، انزلاق من الأسفل أو Fade في Android)، وتصبح نشطة.
أثناء عملية Pop (الضغط على العودة)، تُزال الشاشة العلوية من المكدس، وتُدمر أو تنتقل إلى الذاكرة، وتصبح الشاشة السابقة نشطة. iOS يدمر ViewController المنبثق افتراضياً، محرراً الذاكرة. Android يمكنه حفظ Fragment في مكدس العودة مع إمكانية استعادته دون إعادة إنشاء.
عمق المكدس يؤثر على الأداء: كل شاشة في المكدس تستهلك ذاكرة. يُوصى بعدم الاحتفاظ بأكثر من 10 شاشات في المكدس. للتنقل العميق، استخدم PopToRoot أو أعد تشغيل المكدس بشاشة جذر جديدة. Navigation Component في Android يدير حالة المكدس تلقائياً عبر SavedStateHandle.
UINavigationController هو وحدة تحكم حاوية في iOS تدير مكدساً من UIViewControllers. يعرض تلقائياً شريط تنقل بعنوان الشاشة الحالية وزر العودة. الحركة الافتراضية للـ push هي انزلاق من اليمين إلى اليسار، مما يخلق شعوراً بالانغماس في المحتوى للمستخدم.
تتم إضافة الشاشة عبر pushViewController(_:animated:). يتم الإزالة عبر popViewController(animated:). للعودة إلى الشاشة الجذرية، استخدم popToRootViewController(animated:). يدعم UINavigationController أيضاً الإدارة البرمجية للمكدس عبر الخاصية viewControllers — مصفوفة من جميع وحدات التحكم في المكدس.
يحتوي شريط التنقل على عنوان الشاشة، زر العودة وأزرار إجراء اختيارية. بدءاً من iOS 11، العناوين الكبيرة (prefersLargeTitles) تسمح بعرض العنوان بخط مكبر يتحرك ويتقلص أثناء التمرير. هذا يحسن التسلسل الهرمي للتنقل ويبلغ المستخدم عن القسم الحالي.
let detailVC = DetailViewController()
detailVC.title = "Item Details"
navigationController?.pushViewController(detailVC, animated: true)
// العودة إلى الشاشة السابقة
navigationController?.popViewController(animated: true)
// العودة إلى الجذر
navigationController?.popToRootViewController(animated: true)
Navigation Component هي مكتبة Android Jetpack للتنقل التصريحي. توفر NavController الذي يدير الأجزاء أو شاشات Compose عبر رسم بياني للتنقل (nav_graph). Push في Android مشابه لـ iOS: يُضاف كل Fragment جديد إلى مكدس العودة، وزر العودة يعيد السابق.
NavHost هو حاوية تعرض الوجهة الحالية من NavGraph. NavGraph هو ملف XML يصف جميع شاشات التطبيق والاتصالات بينها. تُحدد الانتقالات عبر actions مع تحديد الوجهة ووسائط اختيارية. يعالج Navigation Component تلقائياً زر العودة النظامي والحركات والحفاظ على الحالة.
لنقل البيانات بين الشاشات، يدعم Navigation Component Safe Args — توليد كود لفئات وسائط آمنة النوع. بدلاً من وضع البيانات يدوياً في Bundle، يعلن المطور عن الوسائط في NavGraph ويستقبلها عبر فئات Directions و Args المولدة. Safe Args يزيل أخطاء عدم تطابق الأنواع ويبسط إعادة الهيكلة.
// تعريف NavGraph في XML
<!-- res/navigation/nav_graph.xml -->
@navigation {
NavHost(startDestination = "list") {
composable("list") { ListScreen() }
composable(
"detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.IntType })
) { DetailScreen(it.arguments()?.getInt("itemId") ?: 0) }
}
}
// التنقل برمجياً
navController.navigate("detail/42")
الجمع بين Bottom Navigation والتنقل Push هو نمط شائع في التطبيقات المحمولة. كل تبويب في Bottom Navigation له مكدس شاشات خاص به. Navigation Component يدعم هذا عبر NavHost لكل تبويب أو NavHost واحد مع رسوم بيانية منفصلة لكل تبويب. تبديل التبويبات لا يعيد تعيين حالة مكدسات push.
Push Presentation و Modal Presentation يحلان مهاماً مختلفة في التنقل. Push مصمم لعرض المحتوى التسلسلي، حيث كل شاشة جديدة تعمق السياق. Modal للمهام المركزة التي تتطلب الإكمال. عملياً، من المهم اختيار النمط الصحيح للسيناريو المحدد.
المعايير الرئيسية للاختيار: إذا كان يجب أن يتمكن المستخدم من العودة بحرية دون فقدان السياق — اختر Push. إذا كانت المهمة مؤقتة وتحجب المحتوى الرئيسي (نموذج، تفويض) — استخدم Modal. خلط الأنماط على شاشة واحدة (Push داخل Modal) مقبول لكن يتطلب تسلسلاً هرمياً بصرياً واضحاً.
من الأخطاء الشائعة استخدام Modal لتسلسلات الشاشات التي تعتبر منطقياً جزءاً من التدفق الرئيسي. على سبيل المثال، معالج إنشاء الطلب (الخطوة 1 → الخطوة 2 → الخطوة 3) من الأفضل تنفيذه عبر Push داخل حاوية مشروطة، بدلاً من سلسلة من النوافذ المشروطة. هذا يحافظ على التسلسل الهرمي للتنقل وقابلية توقع العودة.
دعنا نستعرض تنفيذاً كاملاً للتنقل Push على كلتا المنصتين. مثال Swift يوضح UINavigationController مع إدارة برمجية للمكدس ومعالجة الروابط العميقة. مثال Kotlin يظهر Navigation Component مع NavHost والوسائط والحركات.
class ListViewController: UIViewController {
func showDetail(_ itemId: Int) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let detailVC = storyboard.instantiateViewController(
withIdentifier: "DetailViewController"
) as! DetailViewController
detailVC.itemId = itemId
navigationController?.pushViewController(detailVC, animated: true)
}
}
class DetailViewController: UIViewController {
var itemId: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
title = "Item #\(itemId)"
}
}
@Composable
fun PushNavigationApp() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "list") {
composable("list") {
ListScreen(
onItemClick = { id -> navController.navigate("detail/$id") }
)
}
composable(
"detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.IntType })
) { backStackEntry ->
val itemId = backStackEntry.arguments()?.getInt("itemId") ?: 0
DetailScreen(itemId = itemId, onBack = { navController.popBackStack() })
}
}
}
توضح الأمثلة التنقل Push الأساسي: قائمة → تفاصيل. iOS يستخدم القصص المصورة و UINavigationController مع نقل البيانات يدوياً. Android يستخدم NavHost مع وسائط آمنة النوع وإدارة تلقائية لمكدس العودة. كلا النهجين يدعمان الروابط العميقة والحركات المخصصة والحفاظ على الحالة عند التدوير.
الأسئلة الشائعة
Push يضيف شاشة إلى مكدس UINavigationController مع زر عودة. Present يفتح نافذة مشروطة بدون زر عودة — يجب على المستخدم إغلاقها صراحة. Push مناسب للمحتوى التسلسلي، Present للمهام المركزة. في SwiftUI، Push يقابل NavigationLink، و Present يقابل .sheet.
Navigation Component يوفر طرق popBackStack إلى نقطة محددة و popUpTo لتنظيف المكدس. لمنع تجاوز سعة المكدس، استخدم popUpTo(startDestination) { inclusive = true } قبل navigate. هذا يضمن أن المكدس لا يحتوي على أكثر من 5–7 شاشات في وقت واحد.
نعم، الجمع بين Push و Modal هو نمط قياسي. على سبيل المثال: قائمة → Push للتفاصيل → Modal للتفويض. يُوصى بعدم تضمين Push داخل Modal: النافذة المشروطة يجب ألا تحتوي على تنقل مكدسي. إذا كانت هناك حاجة لتسلسل شاشات داخل نافذة مشروطة، استخدم Push داخل حاوية مشروطة.
الروابط العميقة في التنقل Push تفتح التطبيق على شاشة محددة. iOS يستخدم خطط URL والروابط العامة مع UINavigationController. Android يستخدم Intent Filters مع NavDeepLink. في كلتا الحالتين، يحلل النظام الرابط وينشئ مكدس تنقل إلى الشاشة الهدف، محافظاً على إمكانية العودة.
iOS يستخدم حركة الانزلاق القياسية، التي يمكن تخصيصها عبر UINavigationControllerDelegate. Android Navigation Component يدعم الحركات المخصصة عبر موارد XML (slide_in_right, slide_out_left). لـ Compose، استخدم AnimatedNavHost مع معدل animateItemPlacement.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.