-
-
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 | |
| } | |
| } |
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]
Thanks man, you've helped me a lot ๐