UIDex

ورقة الإجراءات

Action Sheet

UIAlertController .actionSheet

اسمه كمانأكشن شيت، قائمة الإجراءات، ورقة الخيارات، قائمة الأوامر السفلية، شيت الخيارات، ورقة التأكيد، options sheet، confirmation dialog، UIActionSheet، iOS options menu، bottom action menu

ورقة الإجراءات قائمة قصيرة من الأوامر تخصّ عنصراً واحداً بعينه، تصعد من الحافّة السفلية وتحجب ما تحتها إلى أن يختار المستخدم أمراً أو يلغي. أصلها iOS، وهي هناك UIAlertController يُنشأ بـ preferredStyle: .actionSheet. وهي ليست ورقة سفلية (bottom sheet): الورقة السفلية تقبل محتوى حرّاً من نموذج أو خريطة أو قائمة طويلة، وارتفاعها يملكه المستخدم يسحبه بين نقاط توقّف، وقد تبقى على الشاشة بلا طبقة حاجبة أصلاً، أمّا ورقة الإجراءات فارتفاعها ناتج عن عدد أوامرها، ولا تُسحب، وليس لها نسخة غير حاجبة. وهي ليست تنبيهاً (alert): التنبيه يقف في وسط الشاشة ويطرح سؤالاً له جوابان، بينما تعرض ورقة الإجراءات ما يمكن فعله بالشيء الذي فُتحت منه. وهي ليست قائمة (menu): القائمة تتعلّق بالزرّ الذي فتحها وتحمل صفوفها أيقونة أو علامة اختيار أو قائمة فرعية، أمّا ورقة الإجراءات فمثبّتة على الحافّة السفلية مهما كان الذي فتحها، وكلّ صفّ فيها نصّ متمركز ليس غير. وعلى الهاتف هي حاجبة وتنتهي بصفّ إلغاء يفصله فراغ عن بقية الأوامر، وعلى iPad يصير النداء نفسه popover لا يُرسم فيه صفّ الإلغاء. وما إن يدخلها حقل إدخال أو صورة أو أمر سابع حتى يصير اسمها الصحيح ورقة سفلية.

لو قلت عليه…

«القايمة اللي بتطلع من تحت وفيها حذف بالأحمر»«الخيارات اللي بتظهر لما تدوس على النقط التلاتة»«الليستة اللي فيها شارك واحفظ وتحتها إلغاء لوحده»«الحاجة اللي بتقفل لو دوست بره منها»«زرار الحذف الأحمر اللي بيطلع من آخر الشاشة»

العيّنة الحيّة

تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.

٩:٤١
الصورة ٤ من ١٢

آخر أمر نُفّذ: لا شيء بعد

RTLالصفوف متمركزة بعرض الورقة فلا ينعكس فيها شيء، واللمس بالخارج إلغاء

تشريح العنصر: كل جزء واسمه

مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.

برومبت البناء

ابنِ ورقة إجراءات (action sheet) لتطبيق موبايل. اجعلها حاجبة دائماً بطبقة تعتيم تبتلع اللمسات وتخرج ما تحتها من شجرة الوصول، وقدّمها على iOS بـ UIAlertController بنمط ‎.actionSheet‎ أو بـ ‎.confirmationDialog‎ في SwiftUI مع titleVisibility: .visible، وعلى أندرويد بـ ModalBottomSheet يحمل قائمة قصيرة من ListItem، وعلى الويب بعنصر <dialog> يُفتح بـ showModal() ويكتفي بـ aria-labelledby يشير إلى العنوان، فالدور والحجب و Escape وتعطيل بقية الصفحة تأتي من العنصر نفسه ولا تحتاج role="dialog" ولا aria-modal="true". حدّد عدد الأوامر بين اثنين وستّة، وضع الأمر المدمّر في أعلى الورقة بعيداً عن الإلغاء، ورتّب الباقي بحيث يكون الأكثر استعمالاً أقرب إلى الحافّة السفلية. اكتب النمط لا اللون: ‎.destructive‎ في UIKit و role: .destructive في SwiftUI، وضع كلمة الحذف في نصّ الأمر نفسه لأن الأحمر لا يصل إلى قارئ الشاشة. افصل الإلغاء بفراغ حقيقي لا بخطّ رفيع، واجعله إجراءً واحداً فقط بنمط ‎.cancel‎، ونفّذ المعالج نفسه عند اللمس على الطبقة الحاجبة وعند زرّ الرجوع في أندرويد وعند Escape على الويب. قبل العرض على iPad اضبط popoverPresentationController.sourceItem أو sourceView مع sourceRect أو barButtonItem وإلا سقط التطبيق بـ NSGenericException، ولا ترسم صفّ الإلغاء في نسخة الـ popover. استخدم الخصائص المنطقية في كل مكان: padding-inline و margin-inline و inset-inline و text-align: center أو start، ولا تكتب padding-left ولا margin-right ولا text-align: left. واترك ارتفاع الصفّ ينمو مع نصّه بـ min-block-size بدل height، وامنع white-space: nowrap و text-overflow: ellipsis و maxLines = 1 على أسماء الأوامر حتى يلتفّ الاسم العربي الطويل على سطرين. أضف حشواً سفلياً بمقدار env(safe-area-inset-bottom) أو navigationBarsPadding()، واضمن مساحة لمس ٤٤ نقطة لكل صفّ، وأعِد التركيز بعد الإغلاق إلى العنصر الذي فتح الورقة، واحترم prefers-reduced-motion في حركة الصعود.

سلوكه في الاتجاه من اليمين لليسار

الجزء ده حصري عندنا.

الصفوف ليس فيها ما ينعكس

لا ينعكس

صفّ الأمر يأخذ المقاس المنطقي كاملاً ونصّه متمركز، فلا بداية فيه ولا نهاية تتبادلان، و UIAlertAction لا يعطيك مقبض محاذاة أصلاً. والخطر في النسخ المكتوبة يدوياً: text-align: left داخل الصفّ يترك النصّ العربي معلّقاً على اليسار بعد أن انعكست بقية الشاشة. اكتب text-align: center لمطابقة شكل iOS، أو text-align: start إن حاذيت الأسماء إلى بداية السطر كما تفعل صفوف Material.

الأيقونة القائدة

ينعكس

حيث توجد أيقونة قبل الاسم، كصفوف Material أو نسخة ويب مكتوبة يدوياً، فموضعها البداية المنطقية وتنتقل إلى يمين الصفّ في العربية. نفّذ الصفّ بـ flex مع gap و padding-inline-start، ولا تكتب padding-left ولا margin-right، وتذكّر أن أندرويد لا يحرّك شيئاً قبل android:supportsRtl="true" في الـ manifest. والأيقونة الاتجاهية وحدها هي ما ينقلب رسمه أيضاً: خذها من Icons.AutoMirrored في Compose أو من imageFlippedForRightToLeftLayoutDirection() في UIKit، واترك سلّة المهملات ورمز المشاركة كما هما.

الصفّ المدمّر يحتفظ بلونه وموضعه

لا ينعكس

لا شيء في الصفّ المدمّر ينعكس: الأحمر حالة لا اتجاه، وموضعه ثابت في أعلى الورقة في اللغتين. وترتيب الصفوف يجري على المحور الكتلي ولا تمسّه direction، فلا موضع لـ flex-direction: column-reverse قرب هذه القائمة. والخطأ المتكرّر قاعدة شاملة مثل [dir="rtl"] svg { transform: scaleX(-1) } تقلب أيقونة السلّة مع ما تقلب فتظهر مرسومة بالمقلوب، و transform لا نظير منطقيّ له، فاقصر القاعدة على الأيقونات الاتجاهية وحدها.

مرساة الـ popover على iPad

ينعكس

نسخة الجهاز اللوحي تخرج من العنصر الذي فُتحت منه فتتبع جهته المنطقية: زرّ في نهاية شريط التنقّل يفتح الـ popover عند يسار الشاشة في العربية بعد أن كان عند يمينها في الإنجليزية. والفخّ هو sourceRect، فهو CGRect فيزيائي في فضاء إحداثيات العرض المصدر، و CGRect(x: view.bounds.width - 44, …) يبقى مثبّتاً على اليمين مهما كان اتجاه الواجهة. مرّر sourceItem أو barButtonItem ودع UIKit يحسب الإطار، أو اقرأ view.effectiveUserInterfaceLayoutDirection واعكس x بيدك. و permittedArrowDirections فيزيائية بالقدر نفسه، فـ ‎.left‎ و ‎.right‎ بلا مقابل منطقي، فبدّل بينهما مع الاتجاه أو اكتفِ بـ ‎.any‎.

طول الاسم العربي لا يتبع الإنجليزي

لا ينعكس

العربية لا تتبع طول الإنجليزية: «حذف» نصف Delete في عدد المحارف، و«إضافة إلى الشاشة الرئيسية» تزيد على Add to Home Screen بالثلث. والورقة الأصلية تلفّ الاسم الطويل على سطر ثانٍ وتزيد ارتفاع الصفّ، والنسخ المكتوبة يدوياً تكسر ذلك بارتفاع صفّ ثابت، أو بـ white-space: nowrap مع text-overflow: ellipsis، أو بـ maxLines = 1 في Compose. استبدل height بـ min-block-size واترك النصّ يلتفّ، فقصّ آخر الأمر في العربية يحذف المفعول به، وهو الجزء الذي يقول على أيّ شيء يقع الأمر. والعربية تحتاج صندوق سطر أعلى من اللاتينية بالمقاس نفسه، فـ line-height مضبوط على الإنجليزية يقصّ نزول ج و ح والنقطتين تحت ي.

أسماؤه في الكود

كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.

UIKitUIAlertController(title:message:preferredStyle: .actionSheet)الأصل الذي أخذ العنصر اسمه منه؛ الفئة نفسها تعطيك التنبيه المركزي بتبديل preferredStyle إلى ‎.alert‎.
UIKitUIAlertAction(title:style: .destructive)النمط هو ما يصنع الأحمر، وهو لا يُنطق فاكتب التحذير في العنوان؛ ورفيقه ‎.cancel‎ لا يتكرّر أكثر من مرّة في الـ controller الواحد.
UIKitpopoverPresentationController.sourceItemاسم iOS 16 للمرساة، وقبله sourceView مع sourceRect أو barButtonItem؛ غيابه على iPad يعني سقوط التطبيق.
SwiftUI.confirmationDialog(_:isPresented:titleVisibility:)البديل التصريحي من iOS 15؛ الأزرار بداخله تحمل role: .destructive و role: .cancel، ويضيف النظام إلغاءً إن لم تكتبه.
Jetpack ComposeModalBottomSheet { ListItem(…) }أندرويد لا يملك نوعاً باسم action sheet؛ المكافئ ورقة سفلية حاجبة تحمل قائمة قصيرة، ونظيرها في نظام الـ Views هو BottomSheetDialogFragment.
React NativeActionSheetIOS.showActionSheetWithOptionsdestructiveButtonIndex و cancelButtonIndex أرقام داخل مصفوفة options، و anchor هو مرساة iPad؛ الوحدة تعمل على iOS وحده فأندرويد يحتاج بديلاً.
HTML<dialog>.showModal() + ::backdropأقرب تقريب على الويب: الدور والطبقة العليا والتعتيم و Escape وتعطيل بقية الصفحة تأتي جاهزة، فلا تضف غير aria-labelledby.

شوف كمان

آخر تحديث · 2026-08-19