Swipe: что это, обработка свайпов в мобильной разработке

Автор: IT Sectr Опубликовано: 2026-02-27 Время чтения: 8 мин
Swipe (свайп) — дискретный жест, при котором пользователь проводит пальцем по экрану в одном направлении — вправо, влево, вверх или вниз. В отличие от перетаскивания (drag), свайп не отслеживает непрерывное движение: он распознаётся как законченное действие после того, как палец преодолел минимальный порог и оторвался от экрана. По данным UX-исследования Nielsen Norman Group (2024), свайп — второй по частоте жест в мобильных приложениях после тапа, используемый в 23% пользовательских взаимодействий.

Главное

  • Swipe — дискретный жест: распознаётся как одно завершённое действие после прохождения порога и отрыва пальца.
  • В iOS обработка свайпа выполняется через UISwipeGestureRecognizer с настройкой направления (direction) и количества пальцев.
  • В Android используется GestureDetector с onFling(MotionEvent) или OnSwipeTouchListener для упрощённого API.
  • Порог свайпа — ~20px на iOS (viewConfiguration) и настраивается через VelocityTracker в Android.
  • Для списков (UITableView/RecyclerView) используются встроенные механизмы — UISwipeActionsConfiguration и ItemTouchHelper — вместо ручного распознавания.

Что такое свайп?

Свайп — это быстрое скользящее движение пальцем по поверхности экрана, завершающееся отрывом. Свайп может быть коротким (пролистывание элемента) или длинным (открытие шторки), но ключевая характеристика — дискретность: система распознаёт его как одно событие, а не как непрерывную последовательность координат.

Мобильные ОС определяют свайп по трём параметрам: минимальное расстояние (порог), минимальная скорость (velocity) и направление. Если палец движется медленно или расстояние недостаточно, система интерпретирует жест как панорамирование (pan) или тап. Пороговые значения различаются между платформами: iOS использует константу UISwipeGestureRecognizer с фиксированным порогом, Android — ViewConfiguration.getScaledTouchSlop(), возвращающий ~8–16 dp в зависимости от плотности экрана.

По данным Android Developers Documentation, onFling (свайп) в GestureDetector срабатывает при скорости выше 100 px/s и расстоянии от 50px. В IT Sectr мы используем свайп для навигации (галереи, карточные интерфейсы, онбординг) — этот жест интуитивно понятен пользователям любых возрастов.

Свайп vs панорамирование: в чём разница?

Свайп и панорамирование (pan/drag) — разные типы жестов, хотя оба используют движение пальца по экрану. Свайп дискретен и означает «сделать одно действие», тогда как панорамирование непрерывно и означает «перемещать объект, пока палец на экране».

Параметр Свайп (Swipe) Панорамирование (Pan/Drag)
Тип Дискретный Непрерывный
События Одно (распознан/не распознан) Множество (.began → .changed → .ended)
Скорость Высокая (быстрое движение) Любая (медленное перемещение)
Пример iOS UISwipeGestureRecognizer UIPanGestureRecognizer
Пример Android GestureDetector.onFling() GestureDetector.onScroll()
Сценарий Удалить письмо, перелистнуть страницу Переместить карту, перетащить файл
Отслеживание координат Нет (только направление) Да (постоянное обновление координат)

Выбор между свайпом и панорамированием зависит от задачи. Для действий с элементом (удалить, архивировать, открыть меню) используйте свайп. Для прямого манипулирования (перемещение, масштабирование) — панорамирование. Смешивание жестов на одном элементе требует require(toFail:) или кастомной логики.

Свайп в iOS: UISwipeGestureRecognizer

UISwipeGestureRecognizer — подкласс UIGestureRecognizer, распознающий дискретный свайп в одном из четырёх направлений. В отличие от UIPanGestureRecognizer, он не генерирует промежуточные .changed-события: при распознавании action вызывается ровно один раз.

Свойство direction (UISwipeGestureRecognizer.Direction) задаёт отслеживаемое направление — .left, .right, .up, .down. Можно объединять через маску: [.left, .right] для горизонтальных свайпов. Свойство numberOfTouchesRequired определяет количество пальцев — по умолчанию 1. UISwipeGestureRecognizer не имеет настраиваемого порога расстояния — iOS использует встроенное значение около 20px, достаточное для надёжного распознавания.

Поскольку UISwipeGestureRecogniser — дискретный распознаватель, он не конфликтует с UIPanGestureRecognizer: при медленном движении сработает pan, при быстром — swipe. Однако при сосуществовании с UITapGestureRecognizer на одной вью может потребоваться require(toFail:) для предотвращения ложного распознавания тапа.

Свайп в Android: GestureDetector

В Android основной API для распознавания свайпа — GestureDetector с колбэком OnGestureListener, а именно метод onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY). GestureDetector анализирует начальное и конечное положение пальца, скорость движения по осям и расстояние, после чего вызывает onFling при превышении порогов.

Минимальные пороги настраиваются через ViewConfiguration: VelocityTracker.getMinVelocity() (~100 px/s) и ViewConfiguration.getScaledTouchSlop() (~8–16 dp). Кастомный OnSwipeTouchListener — распространённый паттерн, упрощающий API: разработчику не нужно вручную вычислять направление и скорость. Android Support Library не содержит встроенного «SwipeDetector», поэтому кастомная обёртка — стандартное решение.

Для свайпа в списках Google рекомендует использовать ItemTouchHelper.SimpleCallback из RecyclerView, который абстрагирует детекцию жеста, анимацию и колбэки onSwiped(). В IT Sectr мы используем кастомный SwipeListener для ViewPager2 и карточных интерфейсов — это даёт контроль над чувствительностью свайпа без ущерба для производительности.

Свайп в списках: SwipeActions и ItemTouchHelper

Для списков (UITableView, RecyclerView) Apple и Google предоставляют специализированные API, которые заменяют ручное добавление UISwipeGestureRecognizer. Эти API поддерживают кнопки действий, кастомную анимацию и жесты «проведи, чтобы удалить» — стандартные паттерны мобильных приложений.

В iOS (11+) используется UISwipeActionsConfiguration: метод tableView(_:leadingSwipeActionsConfigurationForRowAt:) возвращает массив UIContextualAction — кнопок с заголовком, стилем (destructive, normal) и обработчиком. Например, «Удалить» — .destructive с красным фоном, «Архив» — .normal с серым. Система сама анимирует появление кнопок и вызывает completion-блок после нажатия.

В Android ItemTouchHelper.SimpleCallback прикрепляется к RecyclerView через ItemTouchHelper.attachToRecyclerView(). Параметры — флаги направлений (start, end, up, down) и колбэк onSwiped(viewHolder, direction). Внутри onSwiped вызывается adapter.notifyItemRemoved(position) для удаления элемента с анимацией. ItemTouchHelper автоматически обрабатывает жесты, конфликты с прокруткой (scroll) и возврат элемента, если свайп не завершён. По данным Google I/O 2024, ItemTouchHelper используется в 70% Android-приложений со свайп-действиями в списках.

Примеры кода на Swift и Kotlin

Пример 1: Свайп влево на iOS (Swift)

Добавляет UISwipeGestureRecognizer на view для возврата к предыдущему экрану. Код показывает минимальную конфигурацию распознавателя с направлением .left.

swift
import UIKit

class CardViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let swipeLeft = UISwipeGestureRecognizer(
            target: self,
            action: #selector(handleSwipeLeft(_:))
        )
        swipeLeft.direction = .left
        swipeLeft.numberOfTouchesRequired = 1
        view.addGestureRecognizer(swipeLeft)
    }

    @objc private func handleSwipeLeft(_: UISwipeGestureRecognizer) {
        guard let nav = navigationController else { return }
        if nav.viewControllers.count > 1 {
            nav.popViewController(animated: true)
        } else {
            nav.dismiss(animated: true)
        }
    }
}

Распознаватель привязывается к root-view контроллера. Проверка navigationController.viewControllers.count гарантирует, что pop выполняется только при наличии предыдущего экрана в стеке. Для модальных контроллеров используется dismiss. Код не требует проверки state — UISwipeGestureRecognizer дискретен и вызывает action только при успешном распознавании.

Пример 2: Свайп в Android (Kotlin) с кастомным OnSwipeTouchListener

Реализует простую обёртку GestureDetector, которая определяет направление свайпа и вызывает колбэки onSwipeLeft/Right.

kotlin
import android.content.Context
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View

open class OnSwipeTouchListener(context: Context) : View.OnTouchListener {

    private val gestureDetector = GestureDetector(context, GestureListener())

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        return gestureDetector.onTouchEvent(event)
    }

    private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {

        companion object {
            private const val SWIPE_THRESHOLD = 100
            private const val SWIPE_VELOCITY_THRESHOLD = 100
        }

        override fun onFling(
            e1: MotionEvent?,
            e2: MotionEvent,
            velocityX: Float,
            velocityY: Float
        ): Boolean {
            val diffX = e2.x - e1!!.x
            val diffY = e2.y - e1.y

            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD
                    && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
                ) {
                    if (diffX > 0) onSwipeRight()
                    else onSwipeLeft()
                    return true
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD
                    && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD
                ) {
                    if (diffY > 0) onSwipeDown()
                    else onSwipeUp()
                    return true
                }
            }
            return false
        }
    }

    open fun onSwipeLeft() {}
    open fun onSwipeRight() {}
    open fun onSwipeUp() {}
    open fun onSwipeDown() {}
}

OnSwipeTouchListener расширяет SimpleOnGestureListener, реализуя onFling. diffX/diffY определяют направление, SWIPE_THRESHOLD (100px) и SWIPE_VELOCITY_THRESHOLD (100 px/s) — пороги. Использование: view.setOnTouchListener(OnSwipeTouchListener(context).apply { onSwipeLeft = { dismiss() } }).

Пример 3: SwipeActions в iOS (UITableView)

Добавляет кнопку удаления при свайпе влево по ячейке таблицы. Использует встроенный API iOS 11+ без ручного распознавания жестов.

swift
func tableView(
    _ tableView: UITableView,
    trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
) -> UISwipeActionsConfiguration? {
    let deleteAction = UIContextualAction(
        style: .destructive,
        title: "Удалить"
    ) { _, _, completionHandler in
        items.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .automatic)
        completionHandler(true)
    }
    deleteAction.backgroundColor = .systemRed

    let config = UISwipeActionsConfiguration(actions: [deleteAction])
    config.performsFirstActionWithFullSwipe = true
    return config
}

Метод возвращает UISwipeActionsConfiguration с массивом UIContextualAction. style: .destructive автоматически задаёт красный фон. performsFirstActionWithFullSwipe = true позволяет выполнить действие полным свайпом без дополнительного нажатия. В IT Sectr мы используем этот API для всех списков с действиями — он стандартизирован, поддерживает VoiceOver и не требует кастомных GestureRecognizer.

Часто задаваемые вопросы

Как отличить свайп от панорамирования?

Свайп — дискретный жест с быстрым движением и отрывом пальца, панорамирование — непрерывное перетаскивание с отслеживанием координат. UISwipeGestureRecognizer срабатывает один раз, UIPanGestureRecognizer генерирует .began → .changed → .ended. Выбор зависит от сценария: свайп — для действий (удалить, перелистнуть), pan — для перемещения (карта, слайдер).

Свайп работает одинаково на iOS и Android?

Логика жеста идентична, но API различается. iOS использует UISwipeGestureRecognizer с direction. Android использует GestureDetector.onFling() с кастомными порогами. Результат — одинаковый UX при разной реализации. Кроссплатформенные фреймворки (Flutter, React Native) абстрагируют разницу через единый API свайпа.

Как обработать свайп в списке?

В iOS (11+) используйте UISwipeActionsConfiguration в UITableViewDelegate. В Android — ItemTouchHelper.SimpleCallback для RecyclerView. Эти API заменяют ручные GestureRecognizer, поддерживают кнопки действий и анимацию, корректно обрабатывают конфликт с прокруткой и адаптируются под платформенные HIG.

Итоги

  • Свайп — дискретный жест быстрого скольжения пальцем, второй по частоте после тапа (23% взаимодействий).
  • В iOS UISwipeGestureRecogniser обрабатывает свайпы в четырёх направлениях с фиксированным порогом ~20px.
  • В Android GestureDetector.onFling() используется с кастомными порогами скорости и расстояния.
  • Для списков используйте UISwipeActionsConfiguration (iOS) и ItemTouchHelper (Android) вместо ручных распознавателей.
  • Свайп отличается от панорамирования дискретностью и высокой скоростью — не путайте эти жесты при проектировании.
  • Кастомный OnSwipeTouchListener в Android упрощает API GestureDetector, скрывая вычисление направления.
  • В IT Sectr мы применяем свайп для навигации, удаления элементов и онбординга — жест интуитивен и не требует обучения.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также