MacOSアプリを作るための詳細化技術検討メモ
Sponsor Link
ゴール:SwiftUIのビュー上にドロップされたファイルの情報を取得する
Imageを表示しているビュー上に、別のイメージファイルをドロップして、Imageを入れ替える方法を説明します。
まずは、Imageを表示
後で、別ファイルを読み込んで表示することを考えて、ファイルを読み込んで表示するようなImageを設定します。
struct ContentView: View {
@State var nsImage:NSImage = NSImage(contentsOf: Bundle.main.url(forResource: "View", withExtension: "jpg")! )!
var body: some View {
Image(nsImage: self.nsImage)
.resizable()
.scaledToFit()
}
}
バンドル内に持っている”View.jpg”を読み込んでImageに表示します。
あとで、このImage上にドロップすることを想定してます。
Dropターゲット
SwiftUIでは、ドロップに対応するためのmodifierとして、onDropがあります。
.onDrop(of: [kUTTypeFileURL as String], isTargeted: $isDropTargeted, perform: self.processDroppedFile(provideres:))
第1引数はドロップできるようにするファイルタイプを指定し、2つ目は、このビューにドロップしたかのフラグです(使い方わかりません)。
3つめがドロップされたときに呼ばれる関数です。
関数のプロトタイプは、([NSItemProvider]) -> Bool)。
NSItemProviderから、ドロップされた情報を取得することができます。
ファイルをドロップされたときは、以下のような取得になります。
func processDroppedFile(provideres:[NSItemProvider]) -> Bool {
guard let provider = provideres.first else { return false }
provider.loadItem(forTypeIdentifier: (kUTTypeFileURL as String), options: nil) { (urlData, error) in
DispatchQueue.main.async {
if let urlData = urlData as? Data {
let imageURL = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL
if let localImage = NSImage(contentsOf: imageURL) {
self.nsImage = localImage
}
}
}
}
return true
}
Appleのドキュメントに記載されていますが、UIに影響を与える操作については、DispatchQueue.main.asyncの中で実行するように明記されています。
今回は、設定した値をトリガーとしてSwiftUIが再構築するはずなので、DispatchQueue.main.asyncの中で設定しています。
ドロップされるファイルは、写真等の画像ファイルで、そのファイルからNSImageを生成できることを想定していますが、別タイプのファイルであれば、そのように処理することが必要となります。
Dropしているところ、ファイルをドラッグしていくと、”+”マークが確認できます。
struct ContentView: View {
@State private var isDropTargeted = false
@State var nsImage:NSImage = NSImage(contentsOf: Bundle.main.url(forResource: "View", withExtension: "jpg")! )!
var body: some View {
Image(nsImage: self.nsImage)
.resizable()
.scaledToFit()
.onDrop(of: [kUTTypeFileURL as String], isTargeted: $isDropTargeted, perform: self.processDroppedFile(provideres:))
}
func processDroppedFile(provideres:[NSItemProvider]) -> Bool {
guard let provider = provideres.first else { return false }
provider.loadItem(forTypeIdentifier: (kUTTypeFileURL as String), options: nil) { (urlData, error) in
DispatchQueue.main.async {
if let urlData = urlData as? Data {
let imageURL = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL
if let localImage = NSImage(contentsOf: imageURL) {
self.nsImage = localImage
}
}
}
}
return true
}
}
Drop対応まとめ
onDropの使い方というよりは、NSItemProviderの使い方が難しいかもしれません。
ただ、AppKitでのDrag&Drop対応よりコードは格段に短くなってます。
Sponsor Link
[…] [SwiftUI]Image上でTextをドラッグして動かす […]