ドラッグ&ドロップ — 画面上の視覚オブジェクトを指でドラッグするインタラクティブなジェスチャーです。このジェスチャーは3つのフェーズで構成されます:掴む(長押しまたはタッチ)、移動(指でオブジェクトを動かす)、離す(ターゲットゾーンにドロップ)。要素の並べ替え、フォルダ間のファイル移動、インターフェースの整理(drag to reorder、drag to resize)に使用されます。iOSではUIDragInteractionを介して、AndroidではDragEventとstartDragAndDropを介して実装されます。
重要なポイント
ドラッグ&ドロップ(DnD)は、ユーザーが指やマウスでオブジェクトを掴み、別の位置に移動して離すグラフィカルインターフェース要素の操作方法です。モバイルインターフェースでは、ドラッグ&ドロップは通常長押しで開始されます — ユーザーが要素を300~500ミリ秒間押し続けると、システムがインターフェースをドラッグモードに切り替え、指の動きに沿ってオブジェクトが移動します。
歴史的には、ドラッグ&ドロップはデスクトップインターフェース(Xerox Star、1981年、Macintosh、1984年)でコピー&ペーストコマンドの代替として生まれました。モバイルプラットフォームでは、iOS 11(2017年)のUIDragInteractionとAndroid 7.0(2016年)のマルチウィンドウドラッグ&ドロップで完全サポートが登場しました。それ以前は、モバイルアプリでのドラッグはジェスチャーレコグナイザーを使ったカスタムソリューションで実装されていました。
主要コンセプト:ドラッグセッション、ドラッグアイテム、ドラッグプレビュー(オブジェクトの視覚的表現)、ドロップターゲット、ドロッププロポーザル(ドロップ時のアクション:移動、コピー、リンク)。システムはオペレーティングシステムレベルでセッションを管理し、異なるアプリケーション間でのオブジェクトのドラッグを可能にします(例:ギャラリーからメモへの写真)。
ドラッグ&ドロップは3つの連続したフェーズに分かれます。フェーズ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はSplit ViewおよびSlide Overモード(iPad)でアプリ間のドラッグ&ドロップをサポートしています。AndroidはSplit Screenモードでアプリ間のドラッグをサポートしています。データはUIPasteConfiguration(iOS)またはClipData(Android)を介して転送されます。開発者はサポートされるデータタイプ(iOSの場合はUTI、Androidの場合はMIME)を指定します。
UIDragInteractionはiOS 11で追加された、ドラッグ用の主要なiOS APIです。Viewからのドラッグを有効にするには、UIDragInteractionDelegateとともにUIDragInteractionを追加します。デリゲートは、どの要素がドラッグ可能か、その外観と動作を定義します。要素を受け取るには、UIDropInteractionDelegateとともにUIDropInteractionを使用します。これにより、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は自動的に操作タイプ(.move、.copy、.forbidden)とともにUIDropProposalを呼び出します。操作タイプは視覚的表示に影響します(コピーの場合はプレビューの隅に+アイコン、forbiddenの場合は禁止マーク)。
View.startDragAndDrop()はAPI 24(Android 7.0)以降で利用可能な、ドラッグを開始するAndroid SDKメソッドです。要素を掴んだ後(通常はonLongClick内で)呼び出されます。パラメータ:ClipData(データ)、DragShadowBuilder(視覚的影)、Object myLocalState(ローカル状態)、flags。DragShadowBuilderは指に追随するBitmapを作成します。システムはドラッグ中のアニメーションとタッチイベントを自動的に管理します。
// 並べ替え付き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)。AndroidではItemTouchHelper、iOSではUILongPressGestureRecognizerとセルの並べ替えを介して実装されます。
ファイルマネージャー — フォルダ間でファイルをコピーおよび移動するためのドラッグ&ドロップ。iOS Files App、Android Files by Google、Dropbox — すべてDnDをサポートしています。ファイルをフォルダにドラッグすると内部に移動し(移動)、Option/Ctrlを押しながらドラッグするとコピーされます。ドロップターゲットはホバー時に視覚的にハイライトされ、フォルダの境界が表示されます。
カンバンボード — Trello、Jira、Notionはカードを列間で移動するためにドラッグ&ドロップを使用します(To Do → In Progress → Done)。ドラッグ時にカードは元の列から切り離され、指に追随し、別の列の境界を越えるとアニメーションで挿入されます。iOS 11+はパララックス効果付きのUIDragPreviewをサポートしており、プレビューがタッチポイントからわずかに傾き、奥行きを生み出します。
Springboardセクション(iOS) — iOSホーム画面でのアプリアイコンのドラッグ。アイコンを長押しすると“ジグリング”モードが起動し(アイコンが揺れる)、ユーザーはページ間でアイコンを移動したりフォルダを作成したりできます。これは最も複雑なDnD UXパターンの1つであり、ドラッグ、揺れアニメーション、フォルダグループ化を同時に処理する必要があります。
視覚的フィードバックはドラッグ&ドロップにとって非常に重要です。ドラッグされたオブジェクトは変化する必要があります:縮小(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を使用しています — 標準ボタンを介したアクセス付きです。
よくある質問
ドラッグ&ドロップを開始する標準的なジェスチャーは300~500ミリ秒の長押しです。iOSは組み込みのUIDragInteractionメカニズムを使用し、持続的なタッチを自動的に掴みに変換します。AndroidではOnLongClickListenerコールバックでstartDragAndDrop()を明示的に呼び出す必要があります。一部のケース(ゲームなど)では、DnDは遅延なしの通常タッチで開始できます。
はい、アプリ間ドラッグ&ドロップはiOS 11+(iPad)およびAndroid 7.0+でサポートされています。iPadでは、Split ViewおよびSlide Overモードでドラッグが機能します — ユーザーはSafariからテキストを取得してメモにドロップできます。Androidでは、アプリ間のDnDはSplit Screenで機能します。データはUIPasteConfiguration(iOS)またはClipData(Android)を介して、指定されたサポートタイプ(UTI、MIME)とともに転送されます。
DnDとスクロールの競合は、掴みの遅延によって解決されます。ユーザーが長押しのタイムアウト(500ミリ秒)が切れる前に指を動かし始めると、ジェスチャーはドラッグ&ドロップではなくスクロールとして解釈されます。iOSでは、UIDragInteractionがcancelsTouchesInViewを介してこの競合を自動的に管理します。Androidでは、ItemTouchHelperがしきい値を使用します — 動きがTouchSlopしきい値を超えると、スクロールが優先され長押しがキャンセルされます。
ドラッグ&ドロップは任意のデータタイプをサポートします:テキスト、画像、ファイル、URL、カスタムオブジェクト。iOSはUTIタイプ(public.text、public.image、public.file-url)のNSItemProviderを使用します。AndroidはMIMEタイプ(text/plain、image/png、application/octet-stream)のClipDataを使用します。カスタムデータの場合は、IntentとともにClipData.Itemが作成されます。アプリ間DnDの場合は、データは汎用フォーマット(プレーンテキスト、HTML、RTF、UIImage/PNG)にパッケージ化されます。
iOSでは、戻るアニメーションはUIDragPreviewTargetを介して処理されます — プレビューがアニメーションで戻る最終位置です。Androidでは、translateX/translateYを使用したViewPropertyAnimatorで要素を元の位置に戻します。アニメーションの長さは200~300ミリ秒で、イージングカーブ(EaseOut)を使用します。UIKitは自動的にパララックス効果付きのUIDragPreviewを追加し、ジェスチャーがキャンセルされるとスムーズに戻ります。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。