Gesture Navigation — это система управления мобильным приложением через сенсорные жесты, заменившая аппаратные кнопки на большинстве современных смартфонов. По данным Android Developers (2024), gesture navigation стала стандартом начиная с Android 10, а Apple перешла на жесты ещё с iPhone X в 2017 году. Система включает swipe, pinch, double tap и long press — каждый жест имеет определённое назначение в контексте экрана. Понимание архитектуры жестовой навигации необходимо для создания интуитивных и отзывчивых интерфейсов.
Главное
Gesture Navigation — это метод взаимодействия пользователя с мобильным устройством через последовательности касаний, движений и нажатий, распознаваемые сенсорным экраном. В отличие от традиционных кнопок, жесты не имеют фиксированного положения на экране и распознаются по шаблону движения: свайп от края, pinch двумя пальцами, долгое нажатие.
Переход на жестовую навигацию начался с 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 zoom | View | Нет |
| Контекстные | Long press | 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, расширяющий её hit area без изменения видимого размера. Google рекомендует TouchDelegate для всех интерактивных элементов меньше 48x48dp.
Compose предоставляет модификаторы для обработки жестов: clickable, draggable, swipeable, combinedClickable (для double tap и long press). Внутри Compose использует PointerInputScope для низкоуровневой обработки, но большинству разработчиков достаточно высокоуровневых модификаторов. Для кастомных жестов используется pointerInput с awaitPointerEvent.
iOS SDK использует архитектуру UIGestureRecognizer — абстрактного базового класса, который анализирует последовательность UITouch и определяет, соответствует ли она известному жесту. Apple рекомендует использовать стандартные recognizer везде, где возможно, и только для уникальных жестов создавать кастомные подклассы.
UIKit предоставляет набор готовых recognizer: UITapGestureRecognizer, UISwipeGestureRecognizer, UIPanGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UILongPressGestureRecognizer. Каждый recognizer имеет states (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 — системный recognizer, управляющий свайпом назад в UINavigationController. По умолчанию он активен для всех экранов, кроме корневого. Если ваше приложение использует кастомный NavigationBar, interactivePopGestureRecognizer может перестать работать — потребуется его программная активация через navigationController.interactivePopGestureRecognizer?.delegate.
Конфликт жестов — одна из самых сложных проблем в gesture navigation. Когда пользовательский жест (например, открытие Drawer свайпом от левого края) совпадает с системным жестом (свайп назад на iOS или Android), система должна определить, какой жест имеет приоритет. Правильная обработка этого конфликта критична для UX.
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, который позволяет кастомному recognizer решить, должен ли он начать распознавание. Для Drawer свайпа от левого края можно проверить положение касания: если пользователь тянет Drawer (расстояние более threshold), кастомный жест перехватывает управление. Если жест не распознан, система возвращает управление InteractivePopGestureRecognizer.
Gesture Navigation требует внимательного проектирования, особенно на устройствах с системной жестовой навигацией. Рассмотрим частые ошибки и рекомендации по их устранению.
Самая распространённая ошибка — размещение интерактивных элементов или реализация кастомных свайпов в зонах системных жестов (левый и правый края, нижний край). Пользователь пытается выполнить действие, а вместо этого срабатывает системная навигация. Всегда предусматривайте отступы для системных жестов и обрабатывайте конфликты через exclusion rects.
Параметры жестов (velocity threshold, minimum distance) по умолчанию различаются между платформами. Если ваше приложение кросс-платформенное, не копируйте параметры с одной платформы на другую — тестируйте каждый жест отдельно на Android и iOS. Flutter и React Native автоматически адаптируют некоторые параметры, но не все.
Каждый жест должен сопровождаться визуальной обратной связью: изменение цвета, трансформация, анимация. Пользователь должен понимать, что жест распознан и действие выполняется. На iOS системные recognizer автоматически дают haptic feedback, на Android его нужно добавлять через HapticFeedbackConstants.
Не все пользователи могут выполнять жесты — люди с ограниченной моторикой используют VoiceOver и TalkBack для навигации. Для каждого жеста должна быть кнопочная альтернатива. Google и Apple требуют, чтобы все жестовые действия дублировались доступными для accessibility элементами управления.
Часто задаваемые вопросы
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 для симуляции свайпов в unit-тестах.
Gesture War — конфликт между двумя recognizer, когда оба пытаются обработать одно касание. Избегайте через приоритеты: в iOS используйте require(toFail:), в Compose — sequentialGesture и exclusiveGesture. Flutter использует GestureArena для автоматического разрешения.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также