SwiftUI で Drag & Drop を実装するにはいくつか選択肢があります。
この記事は、 【Sansan × YUMEMI】iOSランチタイムLTで発表した内容をまとめたものになります。
ガイドライン
iOS アプリ開発の起点としては、ヒューマンインターフェイスガイドラインをまず確認します。
ただ、こちらにリンクされているのは、 UIKit での Drag & Drop の API がリンクされていて、 SwiftUI での Drag & Drop へのリンクがなぜかないです。 要件によっては SwiftUI での Drag & Drop の実現は難しい可能性があるため、その部分は UIKit で実装するのも有力な選択肢の一つになります。
SwiftUI 向けの Drag & Drop の公式のドキュメントは以下にあります。
OS ごとの API
OS ごとに利用可能な API とその特徴を表にまとめました。
iOS | API | 実装 | ハンドリング |
---|---|---|---|
iOS 16 | draggable/dropDestination | 不要 | できない |
iOS 14 | onDrag/onDrop | 一部必要 | ドロップはできる |
iOS 13 | DragGesture | 全て+スクロールも | 全てできる |
iOS 16 以上
iOS 16 から Transferable protocol に適合することでアプリ間でのデータのやり取りができるようになります。その延長として、 Drag と Drop が可能な View として SwiftUI 上で表現できるようになっています。
2年前の iOS 16 のリリースに合わせて WWDC2022 で発表されました。
draggable/dropDestination を利用することで簡単に Drag & Drop を実現できます。
draggable で Drag 可能な View になります。
dropDestionation で Drop 可能な View になります。
isTargeted
で Drop 対象になっていることが検知できます。
ただし、基本的に Drag & Drop 時のハンドリングはできません。 Drag 時の preview をカスタマイズすることはできます。
制限事項
LazyVGrid
に付与した dropDestination
はスクロールすると一度破棄されることがあるので、ドラッグしたままスクロールしてドロップしようとするとできないことがあります。 iOS 17.5 ではドラッグした状態でスクロールして再表示した LazyVGrid
に dropDestination
が適用されていないような挙動になります。 iOS 18 では発生しません。また iOS 17.5 でも LazyVGrid
に換えて VStack
+ HStack
でレイアウトすれば再表示しても dropDestination
が適用されていてドラッグ可能になります。
iOS 14 以上
iOS 14 からは NSItemProvider
に適合することで onDrag/onDrop で表現できます。
onDrag で Drag 可能な View になります。
ただし preview が使えるのは iOS 15 からです。
onDrop で Drop 可能な View になります。
基本的に Drag 時のハンドリングはできません。 Drop 時のハンドリングは DropDelegate
が用意されています。
iOS 16 以上がターゲットでも Drop 時のハンドリングが必要な場合はこちらの API を使います。
iOS 13 以上
iOS 13 からは DragGesture で表現できます。
基本的に全てハンドリングできます。
Drag & Drop の実装も書く必要があるのと、スクロールイベントが干渉すると実装が難しいです。
チュートリアル
macOS 向けのチュートリアルですが、 Transferable protocol の実践としては有効な内容になっています。