Drag-and-Drop — інтерактивний жест перетягування візуального об’єкта по екрану пальцем. Жест складається з трьох фаз: захоплення (довге натискання або дотик), переміщення (рух пальця з об’єктом) і відпускання (скидання в цільову зону). Використовується для зміни порядку елементів, переміщення файлів між папками, організації інтерфейсу (drag to reorder, drag to resize). На iOS реалізується через UIDragInteraction, на Android — через DragEvent і startDragAndDrop.
Головне
Drag-and-Drop (DnD) — метод маніпуляції графічними елементами інтерфейсу, при якому користувач захоплює об’єкт пальцем або мишею, переміщує його в іншу позицію та відпускає. У мобільних інтерфейсах Drag-and-Drop зазвичай ініціюється довгим натисканням — користувач утримує елемент протягом 300–500 мс, після чого система перемикає інтерфейс у режим перетягування, і рух пальця переміщує об’єкт слідом.
Історично Drag-and-Drop з’явився в десктопних інтерфейсах (Xerox Star, 1981; Macintosh, 1984) як альтернатива командам копіювати/вставити. На мобільних платформах повноцінна підтримка з’явилася в iOS 11 (2017) з UIDragInteraction та в Android 7.0 (2016) з Multi-Window Drag-and-Drop. До цього перетягування в мобільних додатках реалізовувалося кастомними рішеннями через розпізнавачі жестів.
Ключові концепції: сесія перетягування (drag session), елементи перетягування (drag items), попередній перегляд перетягування (drag preview — візуальний образ об’єкта), ціль скидання (drop target), пропозиція скидання (drop proposal — дія при скиданні: перемістити, скопіювати, зв’язати). Система керує сесією на рівні операційної системи, що дозволяє перетягувати об’єкти між різними додатками (наприклад, фото з Галереї в Нотатки).
Drag-and-Drop поділяється на три послідовні фази. Фаза 1 — Захоплення (Початок перетягування): користувач ініціює жест (зазвичай довге натискання), система створює сесію перетягування, захоплює дані об’єкта та відображає попередній перегляд перетягування — напівпрозору копію елемента, що слідує за пальцем. У цій фазі система відхиляє інші жести (прокручування, виділення тексту) до завершення DnD.
Фаза 2 — Переміщення (Рух перетягування): палець рухається по екрану, попередній перегляд перетягування слідує за точкою дотику. Система безперервно визначає ціль скидання під пальцем і надсилає події drag-enter, drag-move та drag-exit. Ціль скидання може візуально реагувати: підсвічуватися, розширюватися або показувати лінію-вставку. Ця фаза триває від 0,5 до 10+ секунд залежно від складності завдання.
Фаза 3 — Відпускання (Скидання): палець відривається від екрану. Система завершує сесію перетягування та викликає зворотній виклик скидання у цільового елемента. Якщо ціль скидання відхиляє скидання (наприклад, перетягування файлу в папку без прав запису), попередній перегляд перетягування анімаційно повертається на вихідну позицію (анімація скасування). Якщо скидання прийнято, елемент зникає з вихідної позиції та з’являється в цільовій.
| Фаза | Подія iOS | Подія Android | Візуальний стан |
|---|---|---|---|
| Захоплення | UIDragInteractionDelegate.dragSessionWillBegin | View.OnLongClickListener → startDragAndDrop | Попередній перегляд з’являється під пальцем |
| Переміщення | UIDropInteractionDelegate.dragSessionDidUpdate | View.OnDragListener.onDrag (ACTION_DRAG_MOVED) | Попередній перегляд рухається, ціль підсвічується |
| Відпускання | 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)
// Попередній перегляд перетягування — зменшена копія елемента
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. Тип операції впливає на візуальне відображення (значок + у кутку попереднього перегляду для копіювання, заборонний знак для 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 зі зменшеним попереднім переглядом
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 — вбудований механізм, який автоматично обробляє довге натискання для захоплення, анімацію переміщення та утилітарні методи 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). Ціль скидання візуально підсвічується при наведенні, показуючи межі папки.
Канбан-дошки — Trello, Jira, Notion використовують Drag-and-Drop для переміщення карток між колонками (To Do → In Progress → Done). При перетягуванні картка відкріплюється від вихідної колонки, слідує за пальцем і при перетині межі іншої колонки вставляється з анімацією. iOS 11+ підтримує UIDragPreview з паралакс-ефектом, де попередній перегляд злегка відхиляється від точки дотику, створюючи глибину.
Розділи Springboard (iOS) — перетягування іконок додатків на головному екрані iOS. Довге натискання на іконку активує режим «трясіння» (іконки тремтять), після чого користувач може переміщувати іконки між сторінками та створювати папки. Це один із найскладніших UX-патернів DnD, оскільки вимагає одночасної обробки перетягування, анімації трясіння та групування в папки.
Візуальний зворотний зв’язок — критично важливий для Drag-and-Drop. Перетягуваний об’єкт повинен змінитися: зменшитися (iOS: масштаб 0,8–0,9), стати напівпрозорим (альфа 0,7–0,8) і піднятися над інтерфейсом з тінню (shadow offset). Ціль скидання повинна візуально реагувати на наведення: підсвічуватися, розширюватися або показувати межі. Без цих сигналів користувач не розуміє, куди можна скинути об’єкт.
Анімація повернення (скасування перетягування) — якщо користувач відпускає елемент поза зоною скидання або над недійсною зоною, об’єкт повинен анімаційно повернутися на вихідну позицію. iOS UIDragInteractionDelegate надає dragInteraction(_:sessionDidMove:) для відстеження та dragInteraction(_:session:willEndWith:) для анімації повернення. Android View.setOnDragListener отримує ACTION_DRAG_ENDED, де можна запустити анімацію повернення через ValueAnimator або ObjectAnimator.
| Рекомендація | iOS | Android |
|---|---|---|
| Захоплення жестом | Довге натискання (стандарт) | OnLongClickListener |
| Попередній перегляд | UIDragItem.previewProvider | DragShadowBuilder |
| Тактильний зворотний зв’язок | UIImpactFeedbackGenerator | HapticFeedbackConstants.LONG_PRESS |
| Анімація повернення | UIDragPreviewTarget | ViewPropertyAnimator |
| Пропозиція скидання | UIDropProposal (.move/.copy) | ACTION_DROP + прапорці |
| Міжпрограмне DnD | iOS 11+ Split View (iPad) | Android 7.0+ Split Screen |
Доступність: користувачі з моторними обмеженнями можуть не виконати жест перетягування. Надавайте альтернативи через кнопки «Перемістити вгору/вниз» для списків, контекстне меню «Перемістити в папку» для файлів. iOS VoiceOver підтримує ротор-дію «Перетягнути» для переміщення елементів. Android TalkBack надає кастомні дії через AccessibilityNodeInfo.addAction(). Nike Run Club і Apple Health використовують DnD для переупорядкування метрик на дашборді — з доступом через стандартні кнопки.
Часті запитання
Стандартний жест для початку Drag-and-Drop — довге натискання тривалістю 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 і прокручування вирішується через затримку захоплення. Якщо користувач починає рух пальця до завершення тайм-ауту довгого натискання (500 мс), жест інтерпретується як прокручування, а не як Drag-and-Drop. У iOS UIDragInteraction автоматично керує цим конфліктом через cancelsTouchesInView. В Android ItemTouchHelper використовує поріг — якщо рух перевищує поріг TouchSlop, довге натискання скасовується на користь прокручування.
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 дані упаковуються в універсальні формати: звичайний текст, HTML, RTF, UIImage/PNG.
У iOS анімація повернення обробляється через UIDragPreviewTarget — кінцева позиція, до якої анімаційно повертається попередній перегляд. В Android використовуйте ViewPropertyAnimator з translateX/translateY для повернення елемента у вихідну позицію. Час анімації — 200–300 мс з кривою easing (EaseOut). UIKit автоматично додає UIDragPreview з паралакс-ефектом, який плавно повертається при скасуванні жесту.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.