Drag-and-Drop — интерактивный жест перетаскивания визуального объекта по экрану с помощью пальца. Жест состоит из трёх фаз: захват (long press или касание), перемещение (движение пальца с объектом) и отпускание (сброс в целевой зоне). Используется для изменения порядка элементов, перемещения файлов между папками, организации интерфейса (drag to reorder, drag to resize). На iOS реализуется через UIDragInteraction, на Android — через DragEvent и startDragAndDrop.
Главное
Drag-and-Drop (DnD, перетаскивание) — метод манипуляции графическими элементами интерфейса, при котором пользователь захватывает объект пальцем или мышью, перемещает его в другую позицию и отпускает. В мобильных интерфейсах Drag-and-Drop обычно инициируется длительным нажатием (Long Press) — пользователь удерживает элемент в течение 300–500 мс, после чего система переключает интерфейс в режим перетаскивания, и движение пальца перемещает объект следом.
Исторически Drag-and-Drop появился в десктопных интерфейсах (Xerox Star, 1981; Macintosh, 1984) как альтернатива командам копировать/вставить. На мобильных платформах полноценная поддержка появилась в iOS 11 (2017) с UIDragInteraction и в Android 7.0 (2016) с Multi-Window Drag-and-Drop. До этого перетаскивание в мобильных приложениях реализовывалось кастомными решениями через gesture recognizers.
Ключевые концепции: drag session (сессия перетаскивания), drag items (перетаскиваемые объекты), drag preview (визуальный образ объекта), drop target (зона сброса), drop proposal (действие при сбросе: переместить, копировать, связать). Система управляет сессией на уровне операционной системы, что позволяет перетаскивать объекты между разными приложениями (например, фото из Галереи в Заметки).
Drag-and-Drop разделяется на три последовательные фазы. Фаза 1 — захват (Drag Start): пользователь инициирует жест (обычно Long Press), система создаёт drag session, захватывает данные объекта и отображает drag preview — полупрозрачную копию элемента, следующую за пальцем. В этой фазе система отклоняет другие жесты (прокрутка, выделение текста) до завершения DnD.
Фаза 2 — перемещение (Drag Move): палец движется по экрану, drag preview следует за точкой касания. Система непрерывно определяет drop target под пальцем и отправляет события драг-энтири (drag enter), драг-мув (drag move) и драг-экзит (drag exit). Drop target может визуально реагировать: подсвечиваться, расширяться или показывать линию-вставку. Эта фаза длится от 0.5 до 10+ секунд в зависимости от сложности задачи.
Фаза 3 — отпускание (Drop): палец отрывается от экрана. Система завершает drag session и вызывает drop callback у целевого элемента. Если drop target отклоняет сброс (например, перетаскивание файла в папку без прав записи), drag preview анимированно возвращается на исходную позицию (cancel animation). Если сброс принят, элемент исчезает с исходной позиции и появляется в целевой.
| Фаза | Событие iOS | Событие Android | Визуальное состояние |
|---|---|---|---|
| Захват | UIDragInteractionDelegate.dragSessionWillBegin | View.OnLongClickListener → startDragAndDrop | Drag preview появляется под пальцем |
| Перемещение | UIDropInteractionDelegate.dragSessionDidUpdate | View.OnDragListener.onDrag (ACTION_DRAG_MOVED) | Preview движется, target подсвечен |
| Отпускание | UIDropInteractionDelegate.performDrop | View.OnDragListener.onDrag (ACTION_DROP) | Анимация сброса или возврата |
Межприложное перетаскивание — функция, доступная на обеих платформах. iOS поддерживает Drag-and-Drop между приложениями в режиме Split View и Slide Over (iPad). Android поддерживает перетаскивание между приложениями в режиме Split Screen. Данные передаются через UIPasteConfiguration (iOS) или ClipData (Android). Разработчик указывает поддерживаемые типы данных (UTI для iOS, MIME для Android).
UIDragInteraction — основной API iOS для перетаскивания, добавленный в iOS 11. Для включения перетаскивания из View добавьте UIDragInteraction с UIDragInteractionDelegate. Делегат определяет, какие элементы перетаскиваются, их внешний вид и поведение. Для приёма элементов используется UIDropInteraction с UIDropInteractionDelegate, который определяет, может ли View принять сброс и как обработать данные.
// UIDragInteraction — перетаскивание элемента из коллекции
class DragViewController: UIViewController, UIDragInteractionDelegate {
@IBOutlet var dragSourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let dragInteraction = UIDragInteraction(delegate: self)
dragSourceView.addInteraction(dragInteraction)
}
func dragInteraction(_ interaction: UIDragInteraction,
itemsForBeginning session: UIDragSession) -> [UIDragItem] {
guard let data = Data(contentsOf: selectedFileURL) else { return [] }
let itemProvider = NSItemProvider(object: data as NSData)
let dragItem = UIDragItem(itemProvider: itemProvider)
// Drag preview — уменьшенная копия элемента
dragItem.previewProvider = {
let previewView = UIImageView(image: thumbnailImage)
previewView.alpha = 0.8
return UIDragPreview(view: previewView)
}
return [dragItem]
}
}UIDropInteractionDelegate — зеркальный интерфейс для приёма сброса. Метод canHandle(_:) проверяет, поддерживается ли тип данных. sessionDidUpdate(_:) визуально обновляет состояние (подсветка, линия-вставка). performDrop(_:) выполняет финальную обработку данных. iOS автоматически вызывает UIDropProposal с типом операции: .move (переместить), .copy (копировать), .forbidden (запрещено). Тип операции влияет на визуальное отображение (значок + в углу preview для копирования, запрещающий знак для forbidden).
View.startDragAndDrop() — метод Android SDK для начала перетаскивания, доступный с API 24 (Android 7.0). Вызывается после захвата элемента (обычно в onLongClick). Параметры: ClipData (данные), DragShadowBuilder (визуальная тень), Object myLocalState (локальное состояние), flags. DragShadowBuilder создаёт Bitmap, который следует за пальцем. Система автоматически управляет анимацией и сенсорными событиями во время перетаскивания.
// Drag-and-Drop элемента RecyclerView с переупорядочиванием
class ReorderAdapter(private val items: MutableList<String>) :
RecyclerView.Adapter<ReorderAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.setOnLongClickListener {
// Создаём ClipData для передачи данных
val clipData = ClipData.newPlainText("item", items[position])
// Кастомный DragShadowBuilder с уменьшенным preview
val shadowBuilder = object : View.DragShadowBuilder(holder.itemView) {
override fun onProvideShadowMetrics(shadowSize: Point, shadowTouchPoint: Point) {
shadowSize.set(itemView.width, itemView.height / 2)
}
}
holder.itemView.startDragAndDrop(clipData, shadowBuilder, position, 0)
true
}
}
}Приём сброса в Android реализуется через View.setOnDragListener(). Слушатель получает события ACTION_DRAG_STARTED, ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED, ACTION_DROP, ACTION_DRAG_ENDED. В событии ACTION_DROP извлекаются данные из ClipData и выполняется целевое действие. Для RecyclerView с переупорядочиванием рекомендуется ItemTouchHelper.SimpleCallback — встроенный механизм, который автоматически обрабатывает Long Press для захвата, анимацию перемещения элементов и утилитарные методы onMove/onSwiped.
Переупорядочивание списков (drag to reorder) — самый частый сценарий DnD: пользователь перетаскивает элементы списка для изменения их порядка. Примеры: перестановка контактов в избранном, сортировка задач в трекерах (Trello, Todoist), изменение порядка треков в плейлисте (Spotify, Apple Music). Реализуется через ItemTouchHelper в Android или UILongPressGestureRecognizer + перестановка ячеек в iOS.
Файловые менеджеры — Drag-and-Drop для копирования и перемещения файлов между папками. iOS Files App, Android Files by Google, Dropbox — все поддерживают DnD. Перетаскивание файла на папку перемещает его внутрь (move), перетаскивание с зажатой клавишей Option/Ctrl — копирует (copy). Drop target визуально подсвечивается при наведении, показывая границы папки.
Канбан-доски — Trello, Jira, Notion используют Drag-and-Drop для перемещения карточек между колонками (To Do → In Progress → Done). При перетаскивании карточка открепляется от исходной колонки, следует за пальцем, и при пересечении границы другой колонки вставляется с анимацией. iOS 11+ поддерживает UIDragPreview с параллакс-эффектом, где preview слегка отклоняется от точки касания, создавая глубину.
Разделы Springboard (iOS) — перетаскивание иконок приложений на домашнем экране iOS. Долгое нажатие на иконку активирует режим "jiggling" (иконки трясутся), после чего пользователь может перемещать иконки между страницами и создавать папки. Это один из самых сложных UX-паттернов DnD, так как требует одновременной обработки перетаскивания, анимации тряски и группировки в папки.
Визуальная обратная связь — критически важна для Drag-and-Drop. Перетаскиваемый объект должен измениться: уменьшиться (iOS: scale 0.8–0.9), стать полупрозрачным (alpha 0.7–0.8) и подняться над интерфейсом с тенью (shadow offset). Drop target должен визуально реагировать на наведение: подсвечиваться (highlight), расширяться (expand) или показывать границы (border). Без этих сигналов пользователь не понимает, куда можно сбросить объект.
Анимация возврата (drag cancel) — если пользователь отпускает элемент вне зоны сброса или над невалидной зоной, объект должен анимированно вернуться на исходную позицию. iOS UIDragInteractionDelegate предоставляет dragInteraction(_:sessionDidMove:) для отслеживания и dragInteraction(_:session:willEndWith:) для анимации возврата. Android View.setOnDragListener получает ACTION_DRAG_ENDED, где можно запустить анимацию возврата через ValueAnimator или ObjectAnimator.
| Рекомендация | iOS | Android |
|---|---|---|
| Захват жестом | Long Press (default) | OnLongClickListener |
| Drag preview | UIDragItem.previewProvider | DragShadowBuilder |
| Тактильная обратная связь | UIImpactFeedbackGenerator | HapticFeedbackConstants.LONG_PRESS |
| Анимация возврата | UIDragPreviewTarget | ViewPropertyAnimator |
| Drop proposal | UIDropProposal (.move/.copy) | ACTION_DROP + флаги |
| Межприкладное DnD | iOS 11+ Split View (iPad) | Android 7.0+ Split Screen |
Доступность: пользователи с моторными ограничениями могут не выполнить жест перетаскивания. Предоставляйте альтернативы через кнопки "Move Up/Down" для списков, контекстное меню "Move to Folder" для файлов. iOS VoiceOver поддерживает ротор-действие "Drag" для перемещения элементов. Android TalkBack предоставляет кастомные действия через AccessibilityNodeInfo.addAction(). Nike Run Club и Apple Health используют DnD для переупорядочивания метрик на дашборде — с доступом через стандартные кнопки.
Часто задаваемые вопросы
Стандартный жест для начала Drag-and-Drop — Long Press (долгое нажатие) длительностью 300–500 мс. iOS использует встроенный механизм UIDragInteraction, который автоматически конвертирует продолжительное касание в захват. Android требует явного вызова startDragAndDrop() в коллбэке OnLongClickListener. В некоторых случаях (например, в играх) DnD может начинаться с обычного касания без задержки.
Да, межприкладной Drag-and-Drop поддерживается на iOS 11+ (iPad) и Android 7.0+. На iPad перетаскивание работает в режиме Split View и Slide Over — пользователь может взять текст из Safari и перенести в Заметки. В Android DnD между приложениями работает в Split Screen. Данные передаются через UIPasteConfiguration (iOS) или ClipData (Android) с указанием поддерживаемых типов (UTI, MIME).
Конфликт DnD и Scroll решается через задержку захвата. Если пользователь начинает движение пальца до истечения таймаута Long Press (500 мс), жест интерпретируется как прокрутка, а не как Drag-and-Drop. В iOS UIDragInteraction автоматически управляет этим конфликтом через cancelsTouchesInView. В Android ItemTouchHelper использует threshold — если движение превышает порог TouchSlop, Long Press отменяется в пользу прокрутки.
Drag-and-Drop поддерживает любые типы данных: текст, изображения, файлы, URL, кастомные объекты. iOS использует NSItemProvider с UTI-типами (public.text, public.image, public.file-url). Android — ClipData с MIME-типами (text/plain, image/png, application/octet-stream). Для кастомных данных создаётся ClipData.Item с Intent. При межприкладном DnD данные упаковываются в универсальные форматы: plain text, HTML, RTF, UIImage/PNG.
В iOS анимация возврата обрабатывается через UIDragPreviewTarget — финальная позиция, к которой анимированно возвращается preview. В Android используйте ViewPropertyAnimator с translateX/translateY для возврата элемента в исходную позицию. Время анимации — 200–300 мс с кривой easing (EaseOut). UIKit автоматически добавляет UIDragPreview с параллакс-эффектом, который плавно возвращается при отмене жеста.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также