التنقل بالإيماءات هو نظام للتحكم في تطبيق المحمول من خلال الإيماءات اللمسية، ليحل محل الأزرار المادية في معظم الهواتف الذكية الحديثة. وفقًا لـ Android Developers (2024)، أصبح التنقل بالإيماءات هو المعيار بدءًا من Android 10، بينما انتقلت Apple إلى الإيماءات مع iPhone X في عام 2017. يشمل النظام السحب والقرص والنقر المزدوج والضغط المطول — كل إيماءة لها غرض محدد في سياق الشاشة. فهم بنية التنقل بالإيماءات ضروري لإنشاء واجهات بديهية وسريعة الاستجابة.
النقاط الرئيسية
التنقل بالإيماءات هو أسلوب تفاعل المستخدم مع جهاز محمول من خلال تسلسلات من اللمسات والحركات والضغطات التي تتعرف عليها الشاشة اللمسية. على عكس الأزرار التقليدية، ليس للإيماءات موضع ثابت على الشاشة ويتم التعرف عليها من خلال أنماط الحركة: السحب من الحافة، القرص بإصبعين، الضغط المطول.
الانتقال إلى التنقل بالإيماءات بدأ مع iPhone X (2017)، حيث أزالت Apple زر الصفحة الرئيسية تمامًا، مستبدلة إياه بسحب من الحافة السفلية. تبعت Google الاتجاه في Android 10 (2020)، مما قدم للمستخدمين خيارًا: التنقل بثلاثة أزرار، التنقل بزرين، والتنقل الكامل بالإيماءات. وفقًا لـ StatCounter (2025)، أكثر من 75% من أجهزة Android و 95% من أجهزة iOS تستخدم التنقل بالإيماءات.
من الناحية المعمارية، التعرف على الإيماءات يتكون من ثلاث مراحل: الالتقاط (التقاط أحداث اللمس)، التعرف (تحديد نمط الحركة)، والإجراء (تنفيذ الإجراء المعين). على مستوى النظام، يحتوي Android و iOS على معالجات مدمجة لإيماءات التنقل الأساسية — السحب للخلف، الانتقال إلى الشاشة الرئيسية، فتح مبدل التطبيقات. يحتاج المطور فقط إلى دمج إيماءاته الخاصة بشكل صحيح في هذا النظام.
جميع الإيماءات اللمسية يمكن تقسيمها إلى ثلاث فئات حسب الغرض وطريقة التنفيذ. كل نوع له قواعد معالجة خاصة به وتوصيات للاستخدام في الواجهة.
إيماءات التنقل تتحكم في الحركة بين الشاشات: السحب من الحافة اليسرى للعودة (iOS)، السحب لأعلى لفتح مبدل التطبيقات، السحب من الحافة السفلية للعودة إلى الشاشة الرئيسية. هذه الإيماءات يتم معالجتها بواسطة النظام على مستوى النافذة ولا يجب أن تتعارض مع الإيماءات المخصصة داخل التطبيق.
إيماءات التلاعب تغير موضع أو حجم أو اتجاه الكائنات على الشاشة. القرص (التكبير بإصبعين)، التدوير، السحب، التمرير السريع — كل هذه الإيماءات تتم معالجتها على مستوى View أو Composable ولا تؤثر على التنقل النظامي.
الإيماءات السياقية تنشط وظائف إضافية دون الانتقال إلى شاشة أخرى. الضغط المطول (لقائمة السياق)، النقر المزدوج (للإعجاب أو التكبير)، السحب من الحافة (لفتح Drawer). هذه الإيماءات تتطلب التنفيذ الأكثر دقة لأنها قد تتداخل مع إيماءات التنقل النظامية.
| نوع الإيماءة | مثال | مستوى المعالجة | تعارض مع النظام |
|---|---|---|---|
| التنقل | السحب للخلف | النافذة / النظام | أساسي |
| التلاعب | القرص للتكبير | View | لا |
| سياقية | الضغط المطول | View | ممكن |
Android SDK يوفر نظام معالجة إيماءات متعدد المستويات، بدءًا من MotionEvent منخفض المستوى إلى GestureDetector و GestureOverlayView عالي المستوى. اختيار المستوى الصحيح يعتمد على تعقيد الإيماءة ومتطلبات الأداء.
GestureDetector هي فئة عالية المستوى تحول تسلسل MotionEvent إلى إيماءات محددة: onDown و onShowPress و onSingleTapUp و onScroll و onLongPress و onFling. يقوم المطور بتجاوز الطرق المطلوبة من GestureDetector.SimpleOnGestureListener ويستقبل حدثًا تم التعرف عليه جاهزًا. يُوصى بـ GestureDetector لجميع الإيماءات القياسية باستثناء التحجيم — لذلك يوجد ScaleGestureDetector.
val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
override fun onFling(
e1: MotionEvent?, e2: MotionEvent,
velocityX: Float, velocityY: Float
): Boolean {
val deltaX = e2.x - (e1?.x ?: 0f)
return if (Math.abs(deltaX) > Math.abs(e2.y - (e1?.y ?: 0f))) {
if (deltaX > 0) onSwipeRight() else onSwipeLeft()
true
} else false
}
})
view.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) }
TouchDelegate هو آلية لتوسيع منطقة اللمس لـ View. يُستخدم عندما يكون العنصر الهدف أصغر من الحد الأدنى لحجم اللمس 48dp. على سبيل المثال، زر صغير "إغلاق" في زاوية الشاشة يحصل على TouchDelegate يوسع منطقة تأثيره دون تغيير الحجم المرئي. توصي Google بـ TouchDelegate لجميع العناصر التفاعلية الأصغر من 48x48dp.
Compose يوفر معدّلات لمعالجة الإيماءات: clickable و draggable و swipeable و combinedClickable (للنقر المزدوج والضغط المطول). داخليًا، يستخدم Compose PointerInputScope للمعالجة منخفضة المستوى، لكن معظم المطورين يحتاجون فقط إلى المعدّلات عالية المستوى. للإيماءات المخصصة، استخدم pointerInput مع awaitPointerEvent.
iOS SDK يستخدم بنية UIGestureRecognizer — فئة أساسية مجردة تحلل تسلسل UITouch وتحدد ما إذا كان يطابق إيماءة معروفة. توصي Apple باستخدام المعرفات القياسية حيثما أمكن، وإنشاء فئات فرعية مخصصة فقط للإيماءات الفريدة.
UIKit يوفر مجموعة من المعرفات الجاهزة: UITapGestureRecognizer و UISwipeGestureRecognizer و UIPanGestureRecognizer و UIPinchGestureRecognizer و UIRotationGestureRecognizer و UILongPressGestureRecognizer. كل معرف له حالات (possible و began و changed و ended و cancelled و failed) — يتتبع المطور الحالة للتفاعل في مراحل مختلفة من الإيماءة.
let swipeBack = UISwipeGestureRecognizer(
target: self,
action: #selector(handleSwipeBack)
)
swipeBack.direction = .right
view.addGestureRecognizer(swipeBack)
@objc func handleSwipeBack() {
navigationController?.popViewController(animated: true)
}
SwiftUI يستخدم معدّلات إيماءات تصريحية، مشابهة لـ Compose: onTapGesture و onLongPressGesture و MagnificationGesture و RotationGesture و DragGesture. يعالج SwiftUI تلقائيًا التعارضات بين الإيماءات من خلال simultaneousGesture و sequencedGesture و exclusiveGesture — معدّلات تحدد أولوية الإيماءة عند التشغيل المتزامن.
Image("photo")
.gesture(
MagnificationGesture()
.onChanged { scale in
self.currentScale = scale
}
.sequenced(before: DragGesture())
)
InteractivePopGestureRecognizer هو معرف نظامي يتحكم في إيماءة السحب للخلف في UINavigationController. افتراضيًا، يكون نشطًا لجميع الشاشات باستثناء الشاشة الجذرية. إذا كان تطبيقك يستخدم NavigationBar مخصصًا، فقد يتوقف interactivePopGestureRecognizer عن العمل — ستحتاج إلى تنشيطه برمجيًا عبر navigationController.interactivePopGestureRecognizer?.delegate.
تعارض الإيماءات هو أحد أصعب المشكلات في التنقل بالإيماءات. عندما تتزامن إيماءة مخصصة (مثل فتح Drawer بالسحب من الحافة اليسرى) مع إيماءة نظامية (السحب للخلف على iOS أو Android)، يجب على النظام تحديد أي إيماءة لها الأولوية. معالجة هذا التعارض بشكل صحيح أمر بالغ الأهمية لتجربة المستخدم.
Android 10+ يسمح للتطبيق بحجز مناطق من الشاشة لإيماءاته الخاصة عبر WindowInsets. استخدم ViewCompat.setSystemGestureExclusionRects لتحديد المناطق التي لا يجب أن تعمل فيها إيماءات النظام. على سبيل المثال، لـ Drawer على اليسار، يمكنك استبعاد الحافة اليسرى للشاشة (بعرض يصل إلى 200dp) من السحب النظامي للخلف. حددت Google حدًا: يمكن استبعاد ما يصل إلى 200dp على كل جانب.
val exclusionRect = Rect(0, 0, 200, height)
ViewCompat.setSystemGestureExclusionRects(
drawerView,
listOf(SystemGestureExclusionRect(exclusionRect))
)
iOS يوفر الطريقة gestureRecognizerShouldBegin في UIGestureRecognizerDelegate، والتي تسمح للمعرف المخصص بتقرير ما إذا كان يجب أن يبدأ التعرف. لسحب Drawer من الحافة اليسرى، يمكنك التحقق من موضع اللمس: إذا كان المستخدم يسحب Drawer (المسافة تتجاوز العتبة)، تلتقط الإيماءة المخصصة التحكم. إذا لم يتم التعرف على الإيماءة، يعيد النظام التحكم إلى InteractivePopGestureRecognizer.
التنقل بالإيماءات يتطلب تصميمًا دقيقًا، خاصة على الأجهزة التي تستخدم التنقل بالإيماءات النظامي. دعونا نلقي نظرة على الأخطاء الشائعة والتوصيات لإصلاحها.
الخطأ الأكثر شيوعًا هو وضع عناصر تفاعلية أو تنفيذ سحبات مخصصة في مناطق إيماءات النظام (الحافتين اليسرى واليمنى، الحافة السفلية). يحاول المستخدم تنفيذ إجراء، ولكن بدلاً من ذلك يتم تشغيل التنقل النظامي. وفر دائمًا هوامش لإيماءات النظام وتعامل مع التعارضات من خلال exclusion rects.
معلمات الإيماءات (عتبة السرعة، الحد الأدنى للمسافة) تختلف افتراضيًا بين المنصات. إذا كان تطبيقك متعدد المنصات، لا تنسخ المعلمات من منصة إلى أخرى — اختبر كل إيماءة بشكل منفصل على Android و iOS. يقوم Flutter و React Native بتكييف بعض المعلمات تلقائيًا، ولكن ليس كلها.
كل إيماءة يجب أن تكون مصحوبة بتغذية راجعة بصرية: تغيير اللون، التحويل، الرسوم المتحركة. يجب أن يفهم المستخدم أن الإيماءة تم التعرف عليها والإجراء قيد التنفيذ. على iOS، توفر المعرفات النظامية تغذية راجعة لمسية تلقائيًا؛ على Android يجب إضافتها عبر HapticFeedbackConstants.
ليس كل المستخدمين يمكنهم تنفيذ الإيماءات — الأشخاص ذوو المهارات الحركية المحدودة يستخدمون VoiceOver و TalkBack للتنقل. يجب أن يكون لكل إيماءة بديل زر. تطلب Google و Apple تكرار جميع إجراءات الإيماءات بعناصر تحكم متاحة لإمكانية الوصول.
الأسئلة الشائعة
iOS — أدخلت Apple التنقل بالإيماءات مع iPhone X في عام 2017، مستبدلة زر الصفحة الرئيسية بسحب من الحافة السفلية. اتبعت Android المثال في Android 10 (2020)، مقدمة الإيماءات كبديل للأزرار.
السحب هو حركة سريعة بسرعة عالية (بكسل/ثانية) ومتقطع. التمرير هو حركة بطيئة بسرعة منخفضة ومستمر. استخدم velocityX/Y للتمييز: العتبة عادة 500—1000 بكسل/ثانية حسب المنصة.
لا — لا يسمح Android و iOS للتطبيق بتعطيل التنقل بالإيماءات النظامي. يمكنك فقط حجز مناطق الشاشة عبر exclusion rects (Android) أو gestureRecognizerShouldBegin (iOS).
محاكي Android يدعم اللمس المتعدد عبر Ctrl+نقرة (إضافة إصبع ثانٍ). محاكي iOS يستخدم Option+نقرة لإصبعين. يستخدم Flutter test WidgetTester.timedDrag لمحاكاة السحبات في اختبارات الوحدة.
حرب الإيماءات (Gesture War) هي تعارض بين معرفين عندما يحاول كلاهما معالجة نفس اللمسة. تجنبها من خلال الأولويات: في iOS استخدم require(toFail:)، في Compose استخدم sequentialGesture و exclusiveGesture. يستخدم Flutter GestureArena للحل التلقائي.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا