Gesture Navigation — ساختار، انواع و پیاده‌سازی

نویسنده: IT Sectr منتشر شده: 2026-06-10 زمان مطالعه: 10 دقیقه

Gesture Navigation — سیستمی برای کنترل اپلیکیشن موبایل از طریق ژست‌های لمسی است که دکمه‌های سخت‌افزاری را در بیشتر گوشی‌های هوشمند مدرن جایگزین کرده است. به گفته Android Developers (2024)، ناوبری ژستی از Android 10 به بعد به استاندارد تبدیل شد و Apple از iPhone X در سال 2017 به ژست‌ها روی آورد. این سیستم شامل swipe، pinch، double tap و long press است — هر ژست در زمینه صفحه هدف مشخصی دارد. درک معماری ناوبری ژستی برای ایجاد رابط‌های بصری و واکنش‌گرا ضروری است.

نکات اصلی

  • Gesture Navigation — کنترل اپلیکیشن از طریق ژست‌های لمسی به جای دکمه‌ها
  • سه نوع ژست: ناوبری، دستکاری و زمینه‌ای
  • Android 10+ و iOS 7+ از ژست‌ها به عنوان روش ناوبری سیستمی استفاده می‌کنند
  • GestureDetector — کلاس پایه پردازش ژست‌ها در Android View
  • UIGestureRecognizer — کلاس انتزاعی برای همه ژست‌ها در iOS UIKit

Gesture Navigation چیست

Gesture Navigation — روشی برای تعامل کاربر با دستگاه موبایل از طریق توالی لمس‌ها، حرکات و فشارهایی است که توسط صفحه لمسی تشخیص داده می‌شوند. برخلاف دکمه‌های سنتی، ژست‌ها موقعیت ثابتی روی صفحه ندارند و بر اساس الگوی حرکت تشخیص داده می‌شوند: سوایپ از لبه، پینچ با دو انگشت، فشار طولانی.

گذار به ناوبری ژستی با iPhone X آغاز شد (2017)، جایی که Apple دکمه Home را به طور کامل حذف کرد و آن را با سوایپ از لبه پایین جایگزین کرد. Google نیز در Android 10 (2020) از این روند پیروی کرد و به کاربران انتخاب داد: ناوبری سه دکمه‌ای، ناوبری دو دکمه‌ای و ناوبری کامل ژستی. طبق آمار StatCounter (2025)، بیش از 75% دستگاه‌های Android و 95% دستگاه‌های iOS از ناوبری ژستی استفاده می‌کنند.

از نظر معماری تشخیص ژست از سه مرحله تشکیل شده است: Capture (گرفتن رویدادهای لمسی)، Recognition (تعیین الگوی حرکت) و Action (اجرای عمل تعیین شده). در سطح سیستم، Android و iOS پردازشگرهای داخلی برای ژست‌های ناوبری پایه دارند — سوایپ به عقب، رفتن به صفحه اصلی، باز کردن سوئیچر اپلیکیشن‌ها. توسعه‌دهنده فقط باید ژست‌های خود را به درستی در این سیستم ادغام کند.

انواع ژست‌ها در ناوبری موبایل

همه ژست‌های لمسی را می‌توان بر اساس هدف و روش اجرا به سه دسته تقسیم کرد. هر نوع قوانین پردازش و توصیه‌های خاص خود را برای استفاده در رابط کاربری دارد.

ژست‌های ناوبری

ژست‌های ناوبری جابجایی بین صفحه‌ها را کنترل می‌کنند: سوایپ از لبه چپ برای بازگشت (iOS)، سوایپ به بالا برای باز کردن سوئیچر اپلیکیشن‌ها، سوایپ از لبه پایین برای بازگشت به صفحه اصلی. این ژست‌ها توسط سیستم در سطح Window پردازش می‌شوند و نباید با ژست‌های کاربر درون اپلیکیشن تضاد داشته باشند.

ژست‌های دستکاری

ژست‌های دستکاری موقعیت، اندازه یا جهت اشیاء روی صفحه را تغییر می‌دهند. Pinch (بزرگنمایی با دو انگشت)، Rotate (چرخش)، Pan (کشیدن)، Swipe (سوایپ سریع برای ورق زدن) — همه این ژست‌ها در سطح View یا Composabale پردازش می‌شوند و بر ناوبری سیستمی تأثیر نمی‌گذارند.

ژست‌های زمینه‌ای

ژست‌های زمینه‌ای بدون رفتن به صفحه دیگر، عملکردهای اضافی را فعال می‌کنند. Long press (فشار طولانی برای منوی زمینه)، Double tap (ضربه دوبل برای لایک یا بزرگنمایی)، Edge swipe (سوایپ از لبه برای باز کردن Drawer). این ژست‌ها دقیق‌ترین پیاده‌سازی را نیاز دارند زیرا ممکن است با ژست‌های ناوبری سیستمی تداخل داشته باشند.

نوع ژستمثالسطح پردازشتعارض با سیستم
ناوبریسوایپ به عقبWindow / Systemاولیه
دستکاریPinch zoomViewندارد
زمینه‌ایLong pressViewممکن است

Gesture Navigation در Android: GestureDetector و MotionEvent

Android SDK یک سیستم پردازش ژست چندسطحی ارائه می‌دهد، از MotionEvent سطح پایین تا GestureDetector و GestureOverlayView سطح بالا. انتخاب صحیح سطح به پیچیدگی ژست و نیازهای عملکرد بستگی دارد.

GestureDetector — کلاس پایه

GestureDetector — کلاسی سطح بالا است که توالی MotionEvent را به ژست‌های خاص تبدیل می‌کند: onDown، onShowPress، onSingleTapUp، onScroll، onLongPress، onFling. توسعه‌دهنده متدهای مورد نیاز GestureDetector.SimpleOnGestureListener را بازنویسی می‌کند و رویداد تشخیص داده شده آماده را دریافت می‌کند. GestureDetector برای همه ژست‌های استاندارد به جز بزرگنمایی توصیه می‌شود — برای آن ScaleGestureDetector وجود دارد.

kotlin
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 برای اهداف سفارشی

TouchDelegate — مکانیزمی برای گسترش ناحیه لمس برای View. زمانی استفاده می‌شود که عنصر هدف کوچک‌تر از حداقل اندازه لمس 48dp باشد. مثلاً دکمه کوچک «بستن» در گوشه صفحه TouchDelegate دریافت می‌کند که ناحیه ضربه آن را بدون تغییر اندازه ظاهری گسترش می‌دهد. Google TouchDelegate را برای همه عناصر تعاملی کوچک‌تر از 48x48dp توصیه می‌کند.

ژست‌ها در Jetpack Compose

Compose اصلاح‌گرهایی برای پردازش ژست‌ها ارائه می‌دهد: clickable، draggable، swipeable، combinedClickable (برای double tap و long press). در داخل، Compose از PointerInputScope برای پردازش سطح پایین استفاده می‌کند، اما برای اکثر توسعه‌دهندگان اصلاح‌گرهای سطح بالا کافی است. برای ژست‌های سفارشی از pointerInput با awaitPointerEvent استفاده می‌شود.

Gesture Navigation در iOS: UIGestureRecognizer و SwiftUI

iOS SDK از معماری UIGestureRecognizer استفاده می‌کند — کلاس پایه انتزاعی که توالی UITouch را تحلیل می‌کند و مشخص می‌کند آیا با یک ژست شناخته شده مطابقت دارد یا خیر. Apple توصیه می‌کند تا جایی که ممکن است از recognizer استاندارد استفاده کنید و فقط برای ژست‌های منحصر به فرد زیرکلاس‌های سفارشی ایجاد کنید.

UIGestureRecognizer در UIKit

UIKit مجموعه‌ای از recognizer آماده ارائه می‌دهد: UITapGestureRecognizer، UISwipeGestureRecognizer، UIPanGestureRecognizer، UIPinchGestureRecognizer، UIRotationGestureRecognizer، UILongPressGestureRecognizer. هر recognizer حالت‌هایی دارد (possible، began، changed، ended، cancelled، failed) — توسعه‌دهنده حالت را برای واکنش در مراحل مختلف ژست دنبال می‌کند.

swift
let swipeBack = UISwipeGestureRecognizer(
    target: self,
    action: #selector(handleSwipeBack)
)
swipeBack.direction = .right
view.addGestureRecognizer(swipeBack)

@objc func handleSwipeBack() {
    navigationController?.popViewController(animated: true)
}

ژست‌ها در SwiftUI

SwiftUI از اصلاح‌گرهای اعلانی ژست‌ها استفاده می‌کند، مشابه Compose: onTapGesture، onLongPressGesture، MagnificationGesture، RotationGesture، DragGesture. SwiftUI به طور خودکار تعارضات بین ژست‌ها را از طریق simultaneousGesture، sequencedGesture و exclusiveGesture مدیریت می‌کند — اصلاح‌گرهایی که اولویت ژست‌ها را هنگام فعال شدن همزمان تعیین می‌کنند.

swift
Image("photo")
    .gesture(
        MagnificationGesture()
            .onChanged { scale in
                self.currentScale = scale
            }
            .sequenced(before: DragGesture())
    )

InteractivePopGestureRecognizer

InteractivePopGestureRecognizer — recognizer سیستمی که سوایپ به عقب را در UINavigationController مدیریت می‌کند. به طور پیش‌فرض برای همه صفحه‌ها به جز صفحه ریشه فعال است. اگر اپلیکیشن شما از NavigationBar سفارشی استفاده کند، interactivePopGestureRecognizer ممکن است از کار بیفتد — نیاز به فعال‌سازی برنامه‌ریزی شده از طریق navigationController.interactivePopGestureRecognizer?.delegate دارد.

تعارض ژست‌های سیستمی و کاربری

تعارض ژست‌ها — یکی از دشوارترین مسائل در ناوبری ژستی است. وقتی ژست کاربر (مثلاً باز کردن Drawer با سوایپ از لبه چپ) با ژست سیستمی (سوایپ به عقب در iOS یا Android) هم‌پوشانی دارد، سیستم باید تعیین کند کدام ژست اولویت دارد. مدیریت صحیح این تعارض برای تجربه کاربری حیاتی است.

راه‌حل در Android: Insets و SystemGestureExclusionRects

Android 10+ به اپلیکیشن اجازه می‌دهد مناطق صفحه را برای ژست‌های خود از طریق WindowInsets رزرو کند. از ViewCompat.setSystemGestureExclusionRects برای مشخص کردن مناطقی که ژست‌های سیستمی نباید فعال شوند استفاده کنید. مثلاً برای Drawer سمت چپ می‌توان لبه چپ صفحه (عرض تا 200dp) را از سوایپ سیستمی به عقب مستثنی کرد. Google محدودیتی تعیین کرده: می‌توان تا 200dp از هر طرف مستثنی کرد.

kotlin
val exclusionRect = Rect(0, 0, 200, height)
ViewCompat.setSystemGestureExclusionRects(
    drawerView,
    listOf(SystemGestureExclusionRect(exclusionRect))
)

راه‌حل در iOS: UIGestureRecognizerDelegate

iOS متد gestureRecognizerShouldBegin را در UIGestureRecognizerDelegate ارائه می‌دهد که به recognizer سفارشی اجازه می‌دهد تصمیم بگیرد آیا باید تشخیص را شروع کند. برای سوایپ Drawer از لبه چپ می‌توان موقعیت لمس را بررسی کرد: اگر کاربر Drawer را می‌کشد (فاصله بیشتر از threshold)، ژست سفارشی کنترل را می‌گیرد. اگر ژست تشخیص داده نشد، سیستم کنترل را به InteractivePopGestureRecognizer برمی‌گرداند.

اشتباهات رایج و بهترین روش‌ها

Gesture Navigation نیاز به طراحی دقیق دارد، به خصوص در دستگاه‌هایی با ناوبری ژستی سیستمی. بیایید اشتباهات رایج و توصیه‌هایی برای رفع آنها را بررسی کنیم.

اشتباه: نادیده گرفتن مناطق تعارض در لبه‌های صفحه

رایج‌ترین اشتباه — قرار دادن عناصر تعاملی یا پیاده‌سازی سوایپ‌های سفارشی در مناطق ژست‌های سیستمی (لبه‌های چپ و راست، لبه پایین). کاربر سعی می‌کند عملی انجام دهد اما در عوض ناوبری سیستمی فعال می‌شود. همیشه برای ژست‌های سیستمی فاصله در نظر بگیرید و تعارضات را از طریق exclusion rects مدیریت کنید.

اشتباه: سرعت تشخیص متفاوت در Android و iOS

پارامترهای ژست (velocity threshold، minimum distance) به طور پیش‌فرض بین پلتفرم‌ها متفاوت است. اگر اپلیکیشن شما چندپلتفرمی است، پارامترها را از یک پلتفرم به دیگری کپی نکنید — هر ژست را جداگانه در Android و iOS تست کنید. Flutter و React Native برخی پارامترها را به طور خودکار تطبیق می‌دهند، اما نه همه.

بهترین روش: بازخورد بصری

هر ژست باید با بازخورد بصری همراه باشد: تغییر رنگ، تبدیل، انیمیشن. کاربر باید بفهمد که ژست تشخیص داده شده و عمل در حال اجراست. در iOS، recognizer سیستمی به طور خودکار بازخورد لمسی (haptic) می‌دهند، در Android باید آن را از طریق HapticFeedbackConstants اضافه کرد.

بهترین روش: دسترسی‌پذیری برای ژست‌ها

همه کاربران نمی‌توانند ژست‌ها را انجام دهند — افراد با محدودیت حرکتی از VoiceOver و TalkBack برای ناوبری استفاده می‌کنند. برای هر ژست باید یک جایگزین دکمه‌ای وجود داشته باشد. Google و Apple نیاز دارند که همه اقدامات ژستی توسط عناصر کنترلی قابل دسترس تکراری شوند.

سوالات متداول

کدام پلتفرم اولین بار ناوبری ژستی را پیاده‌سازی کرد؟

iOS — Apple ناوبری ژستی را با iPhone X در سال 2017 پیاده‌سازی کرد و دکمه Home را با سوایپ از لبه پایین جایگزین کرد. Android در Android 10 (2020) از این الگو پیروی کرد و ژست‌ها را به عنوان جایگزینی برای دکمه‌ها ارائه داد.

چگونه سوایپ را از اسکرول در پیاده‌سازی سفارشی تشخیص دهیم؟

سوایپ — حرکت سریع با velocity بالا (پیکسل/ثانیه) و ناپیوسته. اسکرول — حرکت آهسته با velocity پایین و پیوسته. برای تشخیص از velocityX/Y استفاده کنید: آستانه معمولاً بسته به پلتفرم 500-1000 px/s است.

آیا می‌توان ناوبری ژستی سیستمی را در اپلیکیشن غیرفعال کرد؟

خیر — Android و iOS به اپلیکیشن اجازه غیرفعال کردن ناوبری ژستی سیستمی را نمی‌دهند. شما فقط می‌توانید مناطق صفحه را از طریق exclusion rects (Android) یا gestureRecognizerShouldBegin (iOS) رزرو کنید.

چگونه ژست‌ها را روی شبیه‌ساز تست کنیم؟

Android Emulator از چندلمسی از طریق Ctrl+کلیک (افزودن انگشت دوم) پشتیبانی می‌کند. iOS Simulator — از طریق Option+کلیک برای دو انگشت. Flutter test از WidgetTester.timedDrag برای شبیه‌سازی سوایپ‌ها در تست‌های واحد استفاده می‌کند.

Gesture War چیست و چگونه از آن جلوگیری کنیم؟

Gesture War — تعارض بین دو recognizer وقتی هر دو سعی می‌کنند یک لمس را پردازش کنند. با اولویت‌ها از آن جلوگیری کنید: در iOS از require(toFail:) استفاده کنید، در Compose — sequentialGesture و exclusiveGesture. Flutter از GestureArena برای حل خودکار استفاده می‌کند.

خلاصه

  • Gesture Navigation — کنترل اپلیکیشن از طریق ژست‌های لمسی، استاندارد شده در Android 10+ و iOS 7+
  • سه دسته ژست: ناوبری (سوایپ‌ها)، دستکاری (pinch، rotate) و زمینه‌ای (long press)
  • GestureDetector در Android و UIGestureRecognizer در iOS — کلاس‌های پایه پردازش ژست
  • Jetpack Compose و SwiftUI اصلاح‌گرهای اعلانی برای همه ژست‌های استاندارد ارائه می‌دهند
  • تعارض ژست از طریق exclusion rects (Android) و gestureRecognizerShouldBegin (iOS) حل می‌شود
  • بازخورد بصری و دسترسی‌پذیری — الزامات اجباری برای ناوبری ژستی
  • پارامترهای ژست بین پلتفرم‌ها متفاوت است — threshold و velocity را مستقیماً کپی نکنید

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید