إجراءات السحب
Swipe Actions.swipeActions / ItemTouchHelper
اسمه كمانسوايب أكشنز، أزرار السحب، إجراءات سحب الصفّ، الأزرار المخبّأة خلف الصفّ، السحب للحذف، إجراءات جانبية للصفّ، swipe buttons، swipe to reveal، row actions، swipe to delete، leading and trailing actions، swipeable list row
إجراءات السحب أزرار جالسة خلف صفّ في قائمة، تنكشف حين يجرّ المستخدم الصفّ أفقياً، ويبقى الصفّ في موضعه من القائمة. لكلّ حافّة من حافّتَي الصفّ مجموعتها الخاصّة: البادئة عادةً للإجراء الخفيف القابل للعكس، والخاتمة للإجراء الأثقل مثل الحذف. وهي ليست السحب للتحديث (pull to refresh)، فذاك يتحرّك على المحور الرأسي ويخصّ القائمة كلّها لا صفّاً بعينه. ولا هي قائمة السياق التي تفتحها الضغطة المطوّلة، لأن تلك قائمة مرئية لها موضع على الشاشة وعناصر مقروءة، بينما لا يظهر من إجراءات السحب حرف واحد قبل أن تبدأ الجرّة. وليست السحب للإزالة (swipe to dismiss)، فهذا الأخير نتيجة واحدة معلّقة على الإيماءة: تتجاوز العتبة فيغادر الصفّ القائمة، ولا يُكشف زرّ أصلاً. والأثر العملي لهذا كلّه أن إجراءات السحب تبقى غير مرئية حتى يعثر عليها أحد بالمصادفة، فكلّ ما تضعه فيها يحتاج طريقاً ثانياً يراه المستخدم بعينه.
لو قلت عليه…
العيّنة الحيّة
تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.
البريد
سارة عبد الله
راجعت الملفّ وعندي ملاحظتان
هيئة النقل
تذكرة رحلتك جاهزة للتحميل
مكتبة الحيّ
موعد إرجاع الكتاب بعد يومين
تشريح العنصر: كل جزء واسمه
مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.
برومبت البناء
سلوكه في الاتجاه من اليمين لليسار
الجزء ده حصري عندنا.
البادئة والخاتمة تتبادلان
ينعكسالحافّتان منطقيتان، فتتبادلان مواضعهما مع اتجاه الواجهة: ما عرّفته leading يظهر عند يمين الشاشة في العربية، وما عرّفته trailing يظهر عند يسارها. هذا سلوك المنصّة الصحيح لا خلل فيه، فـ SwiftUI تأخذ HorizontalEdge في .swipeActions(edge:)، و UIKit يفصل بين tableView(_:leadingSwipeActionsConfigurationForRowAt:) و tableView(_:trailingSwipeActionsConfigurationForRowAt:)، وكلاهما يُحلّ من الاتجاه الفعّال للواجهة. أمّا الفريق الذي كتب مواصفته بكلمتَي «يمين» و«يسار» فسيجد الحذف تحت الإبهام الخطأ بعد التعريب: الحركة التي تعوّد المستخدم أنها «أرشفة» صارت «حذف».
الإزاحة الخام تبقى فيزيائية
لا ينعكسأرقام المؤشّر لا تعرف اتجاه الواجهة ولا تنقلب معه. الفرق بين clientX الحالي وقيمته عند بداية الجرّة موجب عند التحرّك يميناً في الاتجاهين معاً، و dX التي يمرّرها ItemTouchHelper.onChildDraw إزاحة فيزيائية كذلك، ومثلها ما تعيده UIPanGestureRecognizer.translation(in:) لأن نظام إحداثيات العرض لا ينعكس. لذلك على كلّ تنفيذ يدوي أن يضرب الإزاحة في إشارة اتجاه قبل أن يقرّر أيّ حافّة انكشفت: getComputedStyle(el).direction === "rtl" ? -1 : 1 على الويب، و LocalLayoutDirection.current == LayoutDirection.Rtl في Compose، و traitCollection.layoutDirection == .rightToLeft في UIKit؛ أمّا المكوّنات الجاهزة فتتكفّل بهذا عنك، إذ ItemTouchHelper يعرّف START و END النسبيّتين ويحوّلهما إلى LEFT و RIGHT عبر convertToAbsoluteDirection حسب اتجاه التخطيط، بينما LEFT و RIGHT مطلقتان لا تنقلبان أبداً.
رسو اللوح ومحاذاة نصّه
ينعكساللوح المكشوف يرسو على الحافّة التي يخصّها، فثبّت لوح الخاتمة بـ inset-inline-end: 0 ولوح البادئة بـ inset-inline-start: 0 بدل right و left، فينتقلان وحدهما عند الانعكاس. وداخل كلّ زرّ يُبقي text-align: start مع padding-inline-start النصَّ والأيقونةَ في جهة القراءة، أمّا text-align: left فيلصق النصّ العربي بالطرف البعيد من زرّ عرضه ٦٠ بكسل فيبدو خللاً في العرض لا اختياراً في التصميم. أمّا إزاحة الصفّ فوق اللوح فبـ transform، وهي خاصّية فيزيائية بلا مكافئ منطقي في CSS، فاكتبها translateX(calc(var(--reveal) * var(--dir) * -1)) واضبط --dir: -1 داخل [dir="rtl"]، وإلّا انزلق الصفّ فوق اللوح الخطأ وكشف الحافّة المقابلة.
أيقونات الإجراءات
لا ينعكسأيقونات الإجراءات لا تنعكس: سلّة المهملات وصندوق الأرشيف والعلم والجرس رسوم لأشياء مادّية لا يتغيّر معناها باتجاه الصفحة، فقاعدة شاملة مثل [dir="rtl"] svg { transform: scaleX(-1) } تقلبها بلا سبب وتجعلها تبدو مرسومة بالمقلوب. الاستثناء أسهم «ردّ» و«إعادة توجيه» وحدها، ولا ينقلب منها شيء تلقائياً: اضبط android:autoMirrored="true" على الـ vector drawable في أندرويد، واستدعِ imageFlippedForRightToLeftLayoutDirection() على UIImage في iOS، واقصر قاعدة scaleX(-1) على صنف تلك الأيقونة وحدها في الويب، ورموز SF التي لها نسخة RTL هي الوحيدة التي تتكفّل بنفسها.
التصادم مع إيماءة الرجوع
ينعكسإيماءة الرجوع في iOS تبدأ من الحافّة البادئة، ومثلها إجراء الحافّة البادئة، فالتنافس بين interactivePopGestureRecognizer وبينه قائم في العربية كما كان في الإنجليزية: الاثنان ينعكسان معاً ولا يتبدّل غير الجانب. والحالة التي تُوقع الفرق هي المتصفّح، لأن Safari يحسب حافّة الرجوع من لغة واجهة المتصفّح لا من سمة dir في صفحتك، فصفحة عربية داخل Safari إنجليزي تبقى بادئتها يميناً بينما يظلّ شريط الرجوع يساراً. وعلى الويب يمنع touch-action: pan-y المتصفّحَ من سحب الصفّ لكنه لا يلغي إيماءة الحافّة، ولا سبيل إلى إلغائها من الصفحة أصلاً، فابدأ الجرّة بعيداً عن الطرفين معاً، وعلى أندرويد ١٠ فما فوق حيث الطرفان كلاهما منطقة رجوع احجز شريطك بـ View.setSystemGestureExclusionRects.
أسماؤه في الكود
كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.
شوف كمان
آخر تحديث · 2026-08-19