Drag-and-Drop — interaktywny gest przeciągania wizualnego obiektu po ekranie za pomocą palca. Gest składa się z trzech faz: chwyt (long press lub dotknięcie), przesuwanie (ruch palca z obiektem) i puszczenie (zrzucenie w docelowej strefie). Służy do zmiany kolejności elementów, przenoszenia plików między folderami, organizacji interfejsu (drag to reorder, drag to resize). W iOS realizowany poprzez UIDragInteraction, w Androidzie — poprzez DragEvent i startDragAndDrop.
Najważniejsze
Drag-and-Drop (DnD, przeciąganie) — metoda manipulacji graficznymi elementami interfejsu, w której użytkownik chwyta obiekt palcem lub myszą, przenosi go w inne miejsce i puszcza. W interfejsach mobilnych Drag-and-Drop jest zwykle inicjowany długim przytrzymaniem (Long Press) — użytkownik przytrzymuje element przez 300–500 ms, po czym system przełącza interfejs w tryb przeciągania, a ruch palca przesuwa obiekt za sobą.
Historycznie Drag-and-Drop pojawił się w interfejsach desktopowych (Xerox Star, 1981; Macintosh, 1984) jako alternatywa dla poleceń kopiuj/wklej. Na platformach mobilnych pełne wsparcie pojawiło się w iOS 11 (2017) z UIDragInteraction i w Android 7.0 (2016) z Multi-Window Drag-and-Drop. Wcześniej przeciąganie w aplikacjach mobilnych było realizowane przez niestandardowe rozwiązania z użyciem gesture recognizers.
Kluczowe koncepcje: drag session (sesja przeciągania), drag items (przeciągane obiekty), drag preview (wizualny obraz obiektu), drop target (strefa zrzutu), drop proposal (akcja przy zrzucie: przenieś, kopiuj, połącz). System zarządza sesją na poziomie systemu operacyjnego, co umożliwia przeciąganie obiektów między różnymi aplikacjami (np. zdjęcie z Galerii do Notatek).
Drag-and-Drop dzieli się na trzy następujące po sobie fazy. Faza 1 — chwyt (Drag Start): użytkownik inicjuje gest (zwykle Long Press), system tworzy sesję przeciągania, przechwytuje dane obiektu i wyświetla drag preview — półprzezroczystą kopię elementu podążającą za palcem. W tej fazie system odrzuca inne gesty (przewijanie, zaznaczanie tekstu) do zakończenia DnD.
Faza 2 — przesuwanie (Drag Move): palec porusza się po ekranie, drag preview podąża za punktem dotyku. System nieprzerwanie określa drop target pod palcem i wysyła zdarzenia drag enter, drag move i drag exit. Drop target może reagować wizualnie: podświetlać się, rozszerzać lub pokazywać linię-wstawkę. Ta faza trwa od 0.5 do 10+ sekund w zależności od złożoności zadania.
Faza 3 — puszczenie (Drop): palec odrywa się od ekranu. System kończy sesję przeciągania i wywołuje drop callback u docelowego elementu. Jeśli drop target odrzuca zrzut (np. przeciąganie pliku do folderu bez uprawnień zapisu), drag preview animacyjnie wraca do pierwotnej pozycji (cancel animation). Jeśli zrzut jest zaakceptowany, element znika z pierwotnej pozycji i pojawia się w docelowej.
| Faza | Zdarzenie iOS | Zdarzenie Android | Stan wizualny |
|---|---|---|---|
| Chwyt | UIDragInteractionDelegate.dragSessionWillBegin | View.OnLongClickListener → startDragAndDrop | Drag preview pojawia się pod palcem |
| Przesuwanie | UIDropInteractionDelegate.dragSessionDidUpdate | View.OnDragListener.onDrag (ACTION_DRAG_MOVED) | Preview się porusza, target podświetlony |
| Puszczenie | UIDropInteractionDelegate.performDrop | View.OnDragListener.onDrag (ACTION_DROP) | Animacja zrzutu lub powrotu |
Przeciąganie między aplikacjami — funkcja dostępna na obu platformach. iOS obsługuje Drag-and-Drop między aplikacjami w trybie Split View i Slide Over (iPad). Android obsługuje przeciąganie między aplikacjami w trybie Split Screen. Dane są przesyłane przez UIPasteConfiguration (iOS) lub ClipData (Android). Deweloper określa obsługiwane typy danych (UTI dla iOS, MIME dla Androida).
UIDragInteraction — główne API iOS do przeciągania, dodane w iOS 11. Aby włączyć przeciąganie z View, dodaj UIDragInteraction z UIDragInteractionDelegate. Delegat określa, które elementy są przeciągane, ich wygląd i zachowanie. Do przyjmowania elementów używa się UIDropInteraction z UIDropInteractionDelegate, który określa, czy View może przyjąć zrzut i jak przetworzyć dane.
// UIDragInteraction — przeciąganie elementu z kolekcji
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 — zmniejszona kopia elementu
dragItem.previewProvider = {
let previewView = UIImageView(image: thumbnailImage)
previewView.alpha = 0.8
return UIDragPreview(view: previewView)
}
return [dragItem]
}
}UIDropInteractionDelegate — lustrzany interfejs do przyjmowania zrzutu. Metoda canHandle(_:) sprawdza, czy typ danych jest obsługiwany. sessionDidUpdate(_:) wizualnie aktualizuje stan (podświetlenie, linia-wstawka). performDrop(_:) wykonuje końcowe przetwarzanie danych. iOS automatycznie wywołuje UIDropProposal z typem operacji: .move (przenieś), .copy (kopiuj), .forbidden (zabronione). Typ operacji wpływa na wyświetlanie wizualne (ikona + w rogu preview dla kopiowania, znak zakazu dla forbidden).
View.startDragAndDrop() — metoda Android SDK do rozpoczęcia przeciągania, dostępna od API 24 (Android 7.0). Wywoływana po chwycie elementu (zwykle w onLongClick). Parametry: ClipData (dane), DragShadowBuilder (wizualny cień), Object myLocalState (stan lokalny), flags. DragShadowBuilder tworzy Bitmap, który podąża za palcem. System automatycznie zarządza animacją i zdarzeniami dotykowymi podczas przeciągania.
// Drag-and-Drop elementu RecyclerView z zmianą kolejności
class ReorderAdapter(private val items: MutableList<String>) :
RecyclerView.Adapter<ReorderAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.setOnLongClickListener {
// Tworzymy ClipData do przesyłania danych
val clipData = ClipData.newPlainText("item", items[position])
// Niestandardowy DragShadowBuilder z zmniejszonym 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
}
}
}Przyjmowanie zrzutu w Androidzie realizowane jest przez View.setOnDragListener(). Słuchacz otrzymuje zdarzenia ACTION_DRAG_STARTED, ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED, ACTION_DROP, ACTION_DRAG_ENDED. W zdarzeniu ACTION_DROP dane są wyodrębniane z ClipData i wykonywana jest docelowa akcja. Dla RecyclerView z zmienianiem kolejności zalecany jest ItemTouchHelper.SimpleCallback — wbudowany mechanizm, który automatycznie obsługuje Long Press do chwytu, animację przesuwania elementów oraz metody pomocnicze onMove/onSwiped.
Zmienianie kolejności list (drag to reorder) — najczęstszy scenariusz DnD: użytkownik przeciąga elementy listy, aby zmienić ich kolejność. Przykłady: przestawianie kontaktów w ulubionych, sortowanie zadań w trackerach (Trello, Todoist), zmiana kolejności utworów w playliście (Spotify, Apple Music). Realizowane przez ItemTouchHelper w Androidzie lub UILongPressGestureRecognizer + przestawianie komórek w iOS.
Menedżery plików — Drag-and-Drop do kopiowania i przenoszenia plików między folderami. iOS Files App, Android Files by Google, Dropbox — wszystkie obsługują DnD. Przeciągnięcie pliku na folder przenosi go do środka (move), przeciągnięcie z przytrzymanym klawiszem Option/Ctrl — kopiuje (copy). Drop target wizualnie podświetla się po najechaniu, pokazując granice folderu.
Tablice Kanban — Trello, Jira, Notion używają Drag-and-Drop do przenoszenia kart między kolumnami (To Do → In Progress → Done). Podczas przeciągania karta odczepia się od źródłowej kolumny, podąża za palcem, a po przekroczeniu granicy innej kolumny wstawiana jest z animacją. iOS 11+ obsługuje UIDragPreview z efektem paralaksy, gdzie preview lekko odchyla się od punktu dotyku, tworząc głębię.
Sekcje Springboard (iOS) — przeciąganie ikon aplikacji na ekranie głównym iOS. Długie przytrzymanie ikony aktywuje tryb „jiggling” (ikony się trzęsą), po czym użytkownik może przenosić ikony między stronami i tworzyć foldery. To jeden z najtrudniejszych wzorców UX DnD, ponieważ wymaga jednoczesnej obsługi przeciągania, animacji trzęsienia i grupowania w foldery.
Informacja zwrotna wizualna — krytycznie ważna dla Drag-and-Drop. Przeciągany obiekt powinien się zmienić: zmniejszyć (iOS: scale 0.8–0.9), stać się półprzezroczystym (alpha 0.7–0.8) i unieść się nad interfejsem z cieniem (shadow offset). Drop target powinien reagować wizualnie na najechanie: podświetlać się (highlight), rozszerzać (expand) lub pokazywać granice (border). Bez tych sygnałów użytkownik nie rozumie, gdzie można upuścić obiekt.
Animacja powrotu (drag cancel) — jeśli użytkownik puści element poza strefą zrzutu lub nad nieprawidłową strefą, obiekt powinien animacyjnie wrócić do pierwotnej pozycji. iOS UIDragInteractionDelegate udostępnia dragInteraction(_:sessionDidMove:) do śledzenia oraz dragInteraction(_:session:willEndWith:) do animacji powrotu. Android View.setOnDragListener otrzymuje ACTION_DRAG_ENDED, gdzie można uruchomić animację powrotu przez ValueAnimator lub ObjectAnimator.
| Zalecenie | iOS | Android |
|---|---|---|
| Chwyt gestem | Long Press (domyślnie) | OnLongClickListener |
| Drag preview | UIDragItem.previewProvider | DragShadowBuilder |
| Informacja dotykowa | UIImpactFeedbackGenerator | HapticFeedbackConstants.LONG_PRESS |
| Animacja powrotu | UIDragPreviewTarget | ViewPropertyAnimator |
| Drop proposal | UIDropProposal (.move/.copy) | ACTION_DROP + flagi |
| Międzyaplikacyjne DnD | iOS 11+ Split View (iPad) | Android 7.0+ Split Screen |
Dostępność: użytkownicy z ograniczeniami motorycznymi mogą nie być w stanie wykonać gestu przeciągania. Zapewnij alternatywy poprzez przyciski „Przenieś w górę/dół” dla list, menu kontekstowe „Przenieś do folderu” dla plików. iOS VoiceOver obsługuje działanie rotora „Drag” do przenoszenia elementów. Android TalkBack zapewnia niestandardowe działania przez AccessibilityNodeInfo.addAction(). Nike Run Club i Apple Health używają DnD do zmiany kolejności metryk na pulpicie — z dostępem przez standardowe przyciski.
Często zadawane pytania
Standardowym gestem do rozpoczęcia Drag-and-Drop jest Long Press (długie przytrzymanie) trwające 300–500 ms. iOS używa wbudowanego mechanizmu UIDragInteraction, który automatycznie konwertuje długi dotyk na chwyt. Android wymaga jawnego wywołania startDragAndDrop() w callbacku OnLongClickListener. W niektórych przypadkach (np. w grach) DnD może zaczynać się od zwykłego dotknięcia bez opóźnienia.
Tak, międzyaplikacyjny Drag-and-Drop jest obsługiwany w iOS 11+ (iPad) i Android 7.0+. Na iPadzie przeciąganie działa w trybie Split View i Slide Over — użytkownik może wziąć tekst z Safari i przenieść go do Notatek. W Androidzie DnD między aplikacjami działa w Split Screen. Dane są przesyłane przez UIPasteConfiguration (iOS) lub ClipData (Android) z określeniem obsługiwanych typów (UTI, MIME).
Konflikt DnD i Scroll rozwiązuje się poprzez opóźnienie chwytu. Jeśli użytkownik zacznie ruch palca przed upływem limitu czasu Long Press (500 ms), gest jest interpretowany jako przewijanie, a nie Drag-and-Drop. W iOS UIDragInteraction automatycznie zarządza tym konfliktem przez cancelsTouchesInView. W Androidzie ItemTouchHelper używa progu — jeśli ruch przekroczy próg TouchSlop, Long Press jest anulowany na rzecz przewijania.
Drag-and-Drop obsługuje dowolne typy danych: tekst, obrazy, pliki, URL, niestandardowe obiekty. iOS używa NSItemProvider z typami UTI (public.text, public.image, public.file-url). Android — ClipData z typami MIME (text/plain, image/png, application/octet-stream). Dla niestandardowych danych tworzy się ClipData.Item z Intent. Przy międzyaplikacyjnym DnD dane są pakowane w uniwersalne formaty: plain text, HTML, RTF, UIImage/PNG.
W iOS animacja powrotu jest obsługiwana przez UIDragPreviewTarget — końcową pozycję, do której animacyjnie wraca preview. W Androidzie użyj ViewPropertyAnimator z translateX/translateY do powrotu elementu do pierwotnej pozycji. Czas animacji — 200–300 ms z krzywą easing (EaseOut). UIKit automatycznie dodaje UIDragPreview z efektem paralaksy, który płynnie wraca przy anulowaniu gestu.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również