Created
February 14, 2020 18:21
-
-
Save jmcd/ffd9be37fc48331b1d20a484d81bc0ee to your computer and use it in GitHub Desktop.
Checking out drag n' drop in Swift UI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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