
アプリで「カードを別の列に移動したい」「項目を並べ替えたい」「別画面や他アプリへ要素をドラッグしたい」——
そんな直感的な操作を実現するのが .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
