-
-
Save jmcd/ffd9be37fc48331b1d20a484d81bc0ee to your computer and use it in GitHub Desktop.
import SwiftUI | |
struct AnimalView: View { | |
var model: Animal | |
var body: some View { | |
VStack { | |
Image(systemName: model.systemImageName) | |
.resizable() | |
.scaledToFit() | |
.frame(width: 50, height: 50) | |
Text(model.name) | |
} | |
.onDrag { | |
NSItemProvider(object: self.model) | |
} | |
} | |
} | |
struct TrashView: View { | |
@EnvironmentObject var zoo: Zoo | |
var body: some View { | |
Image(systemName: "trash") | |
.resizable() | |
.scaledToFit() | |
.frame(width: 100, height: 100) | |
.onDrop(of: [Animal.typeIdentifier], isTargeted: nil) { ips in | |
guard let ip = ips.first(where: { ip in ip.hasItemConformingToTypeIdentifier(Animal.typeIdentifier) }) else { return false } | |
ip.loadObject(ofClass: Animal.self) { reading, _ in | |
guard let animal = reading as? Animal else { return } | |
DispatchQueue.main.async { | |
self.zoo.amimals.removeAll(where: { a in a.name == animal.name }) | |
} | |
} | |
return true | |
} | |
} | |
} | |
struct ContentView: View { | |
@ObservedObject var zoo = Zoo() | |
var body: some View { | |
HStack { | |
Spacer() | |
VStack { | |
ForEach(zoo.amimals, id: \.name) { animal in | |
AnimalView(model: animal) | |
} | |
} | |
Spacer() | |
TrashView().environmentObject(zoo) | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} | |
// MARK: - Model | |
class Zoo: ObservableObject { | |
@Published var amimals = [ | |
Animal(name: "Hare", systemImageName: "hare.fill"), | |
Animal(name: "Ant", systemImageName: "ant.fill"), | |
Animal(name: "Tortoise", systemImageName: "tortoise.fill"), | |
] | |
} | |
final class Animal: NSObject { | |
public static let typeIdentifier = "dnd.animal" | |
let name: String | |
let systemImageName: String | |
internal init(name: String, systemImageName: String) { | |
self.name = name | |
self.systemImageName = systemImageName | |
} | |
} | |
extension Animal: NSItemProviderReading { | |
static var readableTypeIdentifiersForItemProvider: [String] = [typeIdentifier] | |
static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> Animal { | |
let components = String(data: data, encoding: .utf8)!.split(separator: ",").map(String.init) | |
return Animal(name: components[0], systemImageName: components[1]) | |
} | |
} | |
extension Animal: NSItemProviderWriting { | |
static var writableTypeIdentifiersForItemProvider: [String] = [typeIdentifier] | |
func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress? { | |
completionHandler("\(name),\(systemImageName)".data(using: .utf8), nil) // very terrible encoding and decoding 🙃 | |
let p = Progress(totalUnitCount: 1) | |
p.completedUnitCount = 1 | |
return p | |
} | |
} |
Thanks man, you've helped me a lot 👍
I copied and pasted the code as-is and ran it. However, it does not work. The guard at line 32 fails because the ips array is empty. Can you please advise?
guard let ip = ips.first(where:... <-- ips is empty
I copied and pasted the code as-is and ran it. However, it does not work. The guard at line 32 fails because the ips array is empty. Can you please advise?
guard let ip = ips.first(where:... <-- ips is empty
What if you try to do an if let ip = ips.first...
Hi juandahurt, Thanks for the reply.
As it turns out, I came back to the project after a couple of days, and now the "ips" parameter variable is populated correctly and the code works. I did not change any code, so I cannot explain why it was not working earlier. All good now. :-)
To any future people reading this, this example doesn't work building for macOS Big Sur, Xcode 12.5. You have to add "dnd.animal" as a UTType in your info.plist, you can see how to do that here.
Alternatively, you can just remove the "dnd.animal" and just swap it with a different UTType, I used "UTType.data" and everything worked ok
static let typeIdentifier = UTType.data
static var readableTypeIdentifiersForItemProvider: [String] = [typeIdentifier.identifier]
static var writableTypeIdentifiersForItemProvider: [String] = [typeIdentifier.identifier]
I was looking for an implementation like this! Thanks for sharing!