Ad Code

Simple drag and drop with custom objects in iOS 14

DragAndDrop

Simple drag and drop with custom objects in iOS 14

Simple drag and drop implementation with custom objects in iOS 14.

Demo

Simple drag and drop with custom objects in iOS 14

Making your custom type draggable

  • Start with a Codable object that you want to drag and drop.
struct Bird: Codable {
    let name: String
}
  • Add your custom object info to your Project

Project > Target > Info > Exported Type Identifiers

Simple drag and drop with custom objects in iOS 14

  • Add Providable.swift to your project.

  • Add an extension that conforms your object to Providable

  • Add a new final class Wrapper that inherits from NSObject and conforms to ProvidableWrapper

extension Bird: Providable {
    final class Wrapper: NSObject, ProvidableWrapper {
        /// Add properties here
    }
}
  • Add typealias, item and required init to Wrapper
typealias Item = Bird

let item: Item

required init(_ item: Item) {
    self.item = item
    super.init()
}
  • Add a type name and a custom UTType to match your exported type identifier.
static var name = "bird"
static var uti = UTType("com.yourname.yourappname.bird") ?? .data
  • Add arrays of writable and readable types as UTType
static var writableTypes: [UTType] {
    [uti]
}

static var readableTypes: [UTType] {
    [uti, UTType.plainText]
}
  • Add arrays of identifier strings computed from the UTType arrays.
static var writableTypeIdentifiersForItemProvider: [String] {
    writableTypes.map(\.identifier)
}

static var readableTypeIdentifiersForItemProvider: [String] {
    readableTypes.map(\.identifier)
}
  • Add functions to transform the type to and from NSItemProvider
func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping @Sendable (Data?, Error?) -> Void) -> Progress? {
    do {
        switch typeIdentifier {
        case Self.uti.identifier:
            let data = try JSONEncoder().encode(item)
            completionHandler(data, nil)
        default:
            throw DecodingError.valueNotFound(Bird.self, .init(codingPath: [], debugDescription: "No Birds"))
        }
    } catch {
        completionHandler(nil, error)
    }
    return Progress(totalUnitCount: 100)
}

static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> Self {
    switch typeIdentifier {
    case Self.uti.identifier:
        let bird = try JSONDecoder().decode(Bird.self, from: data)
        return .init(bird)
    case UTType.plainText.identifier:
        let string = String(decoding: data, as: UTF8.self)
        let bird = Bird(name: string)
        return .init(bird)
    default:
        throw DecodingError.valueNotFound(Bird.self, .init(codingPath: [], debugDescription: "No Birds"))
    }
}

Adding drag and drop to your SwiftUI views

Once your type conforms to Providable, adding SwiftUI drag and drop modifiers is easy!

onDrag

.onDrag { bird.provider }

onDrop

.onDrop(of: Bird.Wrapper.readableTypes, isTargeted: nil) { providers, location in
    providers.reversed().loadItems(Bird.self) { bird, error in
        if let bird {
            birds.append(bird)
        }
    }
    return true
}

onInsert(of:)

.onInsert(of: Bird.Wrapper.readableTypes) { index, providers in
    providers.reversed().loadItems(Bird.self) { bird, error in
        if let bird {
            birds.insert(bird, at: index)
        }
    }
}

GitHub

View Github


Post a Comment

0 Comments