我正在尝试在macOS上的SwiftUI中实现拖放,我可以以编程方式生成图像并将其作为可拖动项显示在视图中,或者出于相同目的从Assets.xcassets加载图像。我的代码基于this。我尝试引用this question,但无法正常工作。
我的图像显示正常,但是由于我引用的是图像本身,因此我无法为拖放API返回URL(见下文):
//Value of type 'ContentView.DragableImage' has no member 'url' so I cannot use this
.onDrag { return NSItemProvider(object: self.url as NSURL) }
这是代码。我已经通过代码中的注释指出了这一点:
import SwiftUI
let img1url = Bundle.main.url(forResource: "grapes", withExtension: "png") // < -- CAN pass this in because it is by url
let img2url = Bundle.main.url(forResource: "banana", withExtension: "png")
let img3url = Bundle.main.url(forResource: "peach", withExtension: "png")
let img4url = Bundle.main.url(forResource: "kiwi", withExtension: "png")
struct ContentView: View {
var body: some View {
HStack {
VStack {
//DragableImage(url: img1url!)
//DragableImage(url: img3url!)
DragableImage()
DragableImage()
}
VStack {
//DragableImage(url: img2url!)
// DragableImage(url: img4url!)
DragableImage()
DragableImage()
}
DroppableArea()
}.padding(40)
}
struct DragableImage: View {
//let url: URL
var body: some View {
Image("grapes") //<--- Takes in image without url fine
//Image(nsImage: NSImage(byReferencing: url)) //<--- Taking in image by URL (I don't want that)
.resizable()
.frame(width: 150, height: 150)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.padding(2)
.overlay(Circle().strokeBorder(Color.black.opacity(0.1)))
.shadow(radius: 3)
.padding(4)
.onDrag { return NSItemProvider(object: self.url as NSURL) } //<--- MAIN ISSUE: "Value of type 'ContentView.DragableImage' has no member 'url'" (there is now no URL to reference the image by in the return)
}
}
struct DroppableArea: View {
@State private var imageUrls: [Int: URL] = [:]
@State private var active = 0
var body: some View {
let dropDelegate = MyDropDelegate(imageUrls: $imageUrls, active: $active)
return VStack {
HStack {
GridCell(active: self.active == 1, url: imageUrls[1])
GridCell(active: self.active == 3, url: imageUrls[3])
}
HStack {
GridCell(active: self.active == 2, url: imageUrls[2])
GridCell(active: self.active == 4, url: imageUrls[4])
}
}
.background(Rectangle().fill(Color.gray))
.frame(width: 300, height: 300)
.onDrop(of: ["public.file-url"], delegate: dropDelegate)
}
}
struct GridCell: View {
let active: Bool
let url: URL?
var body: some View {
let img = Image(nsImage: url != nil ? NSImage(byReferencing: url!) : NSImage())
.resizable()
.frame(width: 150, height: 150)
return Rectangle()
.fill(self.active ? Color.green : Color.clear)
.frame(width: 150, height: 150)
.overlay(img)
}
}
struct MyDropDelegate: DropDelegate {
@Binding var imageUrls: [Int: URL]
@Binding var active: Int
func validateDrop(info: DropInfo) -> Bool {
return info.hasItemsConforming(to: ["public.file-url"])
}
func dropEntered(info: DropInfo) {
NSSound(named: "Morse")?.play()
}
func performDrop(info: DropInfo) -> Bool {
NSSound(named: "Submarine")?.play()
let gridPosition = getGridPosition(location: info.location)
self.active = gridPosition
if let item = info.itemProviders(for: ["public.file-url"]).first {
item.loadItem(forTypeIdentifier: "public.file-url", options: nil) { (urlData, error) in
DispatchQueue.main.async {
if let urlData = urlData as? Data {
self.imageUrls[gridPosition] = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL
}
}
}
return true
} else {
return false
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
self.active = getGridPosition(location: info.location)
return nil
}
func dropExited(info: DropInfo) {
self.active = 0
}
func getGridPosition(location: CGPoint) -> Int {
if location.x > 150 && location.y > 150 {
return 4
} else if location.x > 150 && location.y < 150 {
return 3
} else if location.x < 150 && location.y > 150 {
return 2
} else if location.x < 150 && location.y < 150 {
return 1
} else {
return 0
}
}
}
}
我还尝试了以下方法,取得了更大的成功。它会在图像拖动时突出显示,但在拖放时不会显示:
import SwiftUI
let image1 = NSImage(named: "green")!
let image2 = NSImage(named: "blue")!
struct ContentView: View {
var body: some View {
HStack {
VStack {
DragableImage(image: image1)
DragableImage(image: image2)
}
VStack {
DragableImage(image: image1)
DragableImage(image: image2)
}
DroppableArea()
}.padding(40)
}
struct DragableImage: View {
@State var image: NSImage
@State private var dragOver = false
var body: some View {
Image(nsImage: image)
.onDrop(of: ["public.file-url"], isTargeted: $dragOver) { providers -> Bool in
providers.first?.loadDataRepresentation(forTypeIdentifier: "public.file-url", completionHandler: { (data, error) in
if let data = data, let path = NSString(data: data, encoding: 4), let url = URL(string: path as String) {
let imageLocal = NSImage(contentsOf: url)
DispatchQueue.main.async {
self.image = imageLocal!
}
}
})
return true
}
.onDrag {
let data = self.image.tiffRepresentation
let provider = NSItemProvider(item: data as NSSecureCoding?, typeIdentifier: kUTTypeTIFF as String)
provider.previewImageHandler = { (handler, _, _) -> Void in
handler?(data as NSSecureCoding?, nil)
}
return provider
}
.border(dragOver ? Color.red : Color.clear)
}
}
struct DroppableArea: View {
@State private var imageUrls: [Int: URL] = [:]
@State private var active = 0
var body: some View {
let dropDelegate = MyDropDelegate(imageUrls: $imageUrls, active: $active)
return VStack {
HStack {
GridCell(active: self.active == 1, url: imageUrls[1])
GridCell(active: self.active == 3, url: imageUrls[3])
}
HStack {
GridCell(active: self.active == 2, url: imageUrls[2])
GridCell(active: self.active == 4, url: imageUrls[4])
}
}
.background(Rectangle().fill(Color.gray))
.frame(width: 300, height: 300)
.onDrop(of: ["public.file-url"], delegate: dropDelegate)
}
}
struct GridCell: View {
let active: Bool
let url: URL?
var body: some View {
let img = Image(nsImage: url != nil ? NSImage(byReferencing: url!) : NSImage())
.resizable()
.frame(width: 150, height: 150)
return Rectangle()
.fill(self.active ? Color.green : Color.clear)
.frame(width: 150, height: 150)
.overlay(img)
}
}
struct MyDropDelegate: DropDelegate {
@Binding var imageUrls: [Int: URL]
@Binding var active: Int
func validateDrop(info: DropInfo) -> Bool {
return info.hasItemsConforming(to: ["public.file-url"])
}
func dropEntered(info: DropInfo) {
NSSound(named: "Morse")?.play()
}
func performDrop(info: DropInfo) -> Bool {
NSSound(named: "Submarine")?.play()
let gridPosition = getGridPosition(location: info.location)
self.active = gridPosition
if let item = info.itemProviders(for: ["public.file-url"]).first {
item.loadItem(forTypeIdentifier: "public.file-url", options: nil) { (urlData, error) in
DispatchQueue.main.async {
if let urlData = urlData as? Data {
self.imageUrls[gridPosition] = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL
}
}
}
return true
} else {
return false
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
self.active = getGridPosition(location: info.location)
return nil
}
func dropExited(info: DropInfo) {
self.active = 0
}
func getGridPosition(location: CGPoint) -> Int {
if location.x > 150 && location.y > 150 {
return 4
} else if location.x > 150 && location.y < 150 {
return 3
} else if location.x < 150 && location.y > 150 {
return 2
} else if location.x < 150 && location.y < 150 {
return 1
} else {
return 0
}
}
}
}
答案 0 :(得分:2)
与参考问题几乎相同,关键是使用NSImage
作为模型,而不是直接使用Image
作为模型,以便能够访问与{{1 }}:
NSItemProvider