アプリで「カードを別の列に移動したい」「項目を並べ替えたい」「別画面や他アプリへ要素をドラッグしたい」——
そんな直感的な操作を実現するのが .draggable
です。
SwiftUIのViewに付けるだけで、ユーザーはその要素をつまんで他の場所へドラッグできるようになります。
.draggableとは?
.draggable
は SwiftUI の修飾子で、指定したビューをドラッグの“出発点(ドラッグ元)”にします。
ドラッグ中に運ぶ実データ(テキストや画像、独自モデルなど)は、SwiftUI では Transferableという仕組みで宣言的に表現できます。
受け取り側(ドロップ先)は .dropDestination
を使います。
主な特徴
-
宣言的でシンプル
View.draggable(_:)
を付けるだけでドラッグ可能になるため、複雑なコードは不要です。 -
型安全
Transferable
準拠の自作型をそのまま運べるので、受け取り時に型変換を行う必要がありません。 -
UIプレビューのカスタム
ドラッグ中に表示するプレビュー(サムネイル)を自由にデザインでき、見た目や操作感を向上できます。 -
アプリ間連携も可能
適切なUTType
を設定すれば、他アプリへもデータをドラッグして渡せます。 -
ドロップと組み合わせ可能
.dropDestination(for:)
とセットで使えば、UI間やアプリ間の受け渡しが完成します。
基本的な使い方
1. 最小例:独自型をそのままドラッグ(Transferable)
自作モデル Note
をドラッグ可能にします。
まずは「どうやって外部とやり取りするか」を Transferable
で宣言します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import SwiftUI import UniformTypeIdentifiers struct Note: Identifiable, Hashable, Codable, Transferable { var id = UUID() var text: String static var transferRepresentation: some TransferRepresentation { // 同一アプリ/同型間では完全復元 CodableRepresentation(contentType: .data) // 他アプリ向けのフォールバック(プレーンテキスト) DataRepresentation(exportedContentType: .plainText) { note in Data(note.text.utf8) } importing: { data in Note(text: String(decoding: data, as: UTF8.self)) } } } |
この Note
を .draggable(note)
でドラッグ元にできます。
1 2 3 4 5 6 7 8 9 10 |
struct DraggableNoteRow: View { let note: Note var body: some View { Text(note.text) .padding(8) .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 8)) .draggable(note) // ← これだけでドラッグ可能 } } |
ポイント
transferRepresentation
で複数形式を用意すると互換性が上がる(まずは完全復元、だめならテキスト、の順)- コードはView側が短く、転送仕様は型側に集約できて保守しやすい
2. ドロップと連携(.dropDestination)
ドラッグした Note
を別の領域にドロップして受け取ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
struct NotesBoard: View { @State private var notes: [Note] = [ Note(text: "りんご"), Note(text: "バナナ"), Note(text: "みかん") ] @State private var inbox: [Note] = [] var body: some View { HStack(spacing: 24) { // 左: ドラッグ元 VStack(alignment: .leading) { Text("ドラッグ元").font(.headline) ForEach(notes) { note in DraggableNoteRow(note: note) } } // 右: ドロップ先 VStack(alignment: .leading) { Text("ドロップ先").font(.headline) ForEach(inbox) { note in Text("受領: \(note.text)") .padding(8) .background(Color.green.opacity(0.2), in: RoundedRectangle(cornerRadius: 8)) } } .frame(minWidth: 220, minHeight: 160) .dropDestination(for: Note.self) { items, _ in inbox.append(contentsOf: items) // 複数ドロップにも自然対応 return true } } .padding() } } |
ポイント
items
は配列:複数アイテムの同時ドロップに対応- 返り値
true
は「受理したよ」の合図。失敗時はfalse
を返す - 同一アプリ間は
Codable
で完全復元、他アプリは.plainText
で互換性確保
3. ドラッグ中プレビューをカスタマイズ(見た目/情報をリッチに)
ドラッグ時に表示されるサムネイルをカスタムできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct FancyDraggableNoteRow: View { let note: Note var body: some View { Text(note.text) .padding(10) .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 10)) .draggable(note) { // プレビュー(ドラッグ中に指先付近へ表示) Label(note.text, systemImage: "note.text") .padding(8) .background(.thickMaterial, in: RoundedRectangle(cornerRadius: 12)) } } } |
ポイント
- 画像やラベル、色など自由にプレビューを設計でき、UXが上がる
- ドラッグ元のタップターゲットとプレビューは別UIにできる
活用シーンと実践例
.draggable
は、単なる要素の移動だけでなく、さまざまなアプリ体験を向上させるために使えます。
特に Transferable
と .dropDestination
を組み合わせることで、型安全で柔軟なドラッグ&ドロップ機能を短いコードで実装できます。
ここでは代表的な活用例を紹介します。
かんばんボード
- タスク管理やプロジェクトボードで、カード(ToDo)を列間にドラッグして状態を変更できます。
- マウスや指で直感的に移動できるため、進捗管理がスムーズになり、操作ログや履歴機能と組み合わせることで業務効率も向上します。
メディア整理
- 画像・音声・ドキュメントなどのメディアファイルを、フォルダやカテゴリ欄にドラッグして整理できます。
UTType
を適切に設定すれば、他アプリやFinder/ファイルアプリとも連携でき、クロスアプリでの資産管理が容易になります。
一括操作
- 複数の項目を選択してまとめてドラッグすることで、一度にコピーや移動を実行できます。
items
が配列として受け取れるため、処理側で複数データを一括登録・削除などに展開でき、UI操作の手間を減らせます。
他アプリ連携
- テキストや画像をドラッグしてメール・メモ・Photosなどの他アプリに直接ドロップできます。
- 適切な
DataRepresentation
やFileRepresentation
を用意すれば、互換性の高い形式で受け渡しが可能になり、アプリ間の情報共有がスムーズになります。
独自並べ替え
- ドロップ時の
location
(座標)を活用して、受け取り配列の挿入位置を自由に決定できます。 - これにより、リストやグリッドの高度な並べ替え機能を実装でき、ユーザーは好みの順序にコンテンツを配置できます。
注意点
便利な .draggable
ですが、いくつかの実装上の注意があります。
以下のポイントを押さえておくことで、より安定したドラッグ&ドロップ機能を提供できます。
- OSバージョン
Transferable
と.dropDestination
が本格的に使えるのは iOS 16 以降です。対象OSの下限設定を確認し、古いバージョン向けには代替手段(長押しメニューや従来のドラッグAPI)を用意するのが安心です。 - データサイズ
大きな動画や高解像度画像などを直接DataRepresentation
で送ると、メモリを圧迫します。その場合はFileRepresentation
を使って一時ファイル経由で転送することで、処理の安定性を確保できます。 - UTTypeの整合
他アプリとやり取りする場合はexportedContentType
を正しく設定することが重要です。テキストなら.plainText
、画像なら.png
や.jpeg
など、受け取り先が理解できる形式を選びましょう。 - フォールバック設計
互換性を高めるには、まずCodableRepresentation
で完全復元を試み、できない場合はDataRepresentation
など汎用形式に切り替える多段構成にします。これにより、同一アプリ間・他アプリ間の両方で安定した転送が可能になります。 - ドロップロジック
ドロップ後の処理では、配列への挿入位置や重複チェック、取り消し(Undo)機能などの業務ロジックを組み込む必要があります。特に並べ替えや複数同時ドロップでは、意図しない順序やデータの重複が発生しないよう注意しましょう。
まとめ
.draggable
は、SwiftUI で直感的なドラッグ体験を簡潔なコードで実装できる強力な仕組みです。
Transferable
と .dropDestination
をセットで設計すれば、型安全で拡張しやすいデータ受け渡しが実現します。
UI間・アプリ間をまたぐドラッグ操作が必要になったら、まず .draggable
を検討しましょう。
参考リンク
View.draggable(_:preview:) | Apple Developer Documentation
View.dropDestination(for:isTargeted:action:) | Apple Developer Documentation
Transferable | Apple Developer Documentation