I am trying to implement Drag and Drop into my application which shares images.
All my images are high performance thumbnails (i.e. small size) so I can't use them as my UIDragItem
, at least not as the final image.
What I am looking for is a way of providing the URL for my original image and send that off as the UIDragItem
and then have the destination fetch the image asynchronously. This is done in the Photos app when an image is stored in iCloud, so it must somehow be possible, I just cant seem to figure out how.
Turns out the solution is quite simple and is described in session 227 Data Delivery with Drag and Drop during this WWDC.
You basically make whatever object you want to drag conform to NSItemProviderWriting, and then implement two things.
NSItemProviderWriting:
The interface for supporting initialization of an item provider based on an object, used by a source app when providing copied or dragged items.
Implement writableTypeIdentifiersForItemProvider
which will give your receiver an idea of what type of object you are providing. This is an array of type identifiers with decreasing fidelity (they describe this well in the video)
Implement loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress?
which does the heavy lifting, this will get called as the receiver tries to load the object you are providing.
You can disregard the specifics of the data fetching below (I am using firebase) but using the native URLSession API would work the same way pretty much.
extension Media: NSItemProviderWriting {
//Provide the types you want you are supplying
static var writableTypeIdentifiersForItemProvider: [String] {
return [(kUTTypeImage as String)]
}
func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress? {
print("Item provider would like to write item from path: \(metadata.path!)")
guard let path = metadata.path else { return nil }
//Allow a maximum of ~30mb to be downloaded into memory if images, 1GB if video.
let maxSize:Int64 = (isVideo ? 1000 : 30) * 1024 * 1024
let storage = Storage.storage().reference(withPath: path)
let progress = Progress(totalUnitCount: 100)
var shouldContinue = true
//When the receiver cancels this block is called where we will set the `shouldContinue` to false to cancel the current task
progress.cancellationHandler = {
shouldContinue = false
}
let task = storage.getData(maxSize: maxSize) { data, error in
//Once the data is fetched or we encounter an error, call the completion handler
completionHandler(data, error)
}
if !shouldContinue {
task.cancel()
}
task.observe(.progress) { snapshot in
if let p = snapshot.progress {
progress.completedUnitCount = Int64(p.fractionCompleted * 100)
}
}
task.observe(.success) { snapshot in
print(snapshot)
}
task.observe(.failure) { snapshot in
print(snapshot)
}
return progress
}
}
Then in our DragDelegate:
@available(iOS 11, *)
extension GridViewDelegateDataSource: UICollectionViewDragDelegate {
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let mediaItem = media[indexPath.item]
//You can now instantiate an NSItemProvider directly from your object because it conforms to the `NSItemProviderWriting` protocol
let itemProvider = NSItemProvider(object: mediaItem)
let dragItem = UIDragItem(itemProvider: itemProvider)
return [dragItem]
}
}
This code for Drag a PHAsset
extension PHAsset : NSItemProviderWriting {
public static var writableTypeIdentifiersForItemProvider: [String] {
return UIImage.writableTypeIdentifiersForItemProvider
}
public func loadData(withTypeIdentifier typeIdentifier: String,
forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress? {
PHImageManager.default().requestImageData(for: self, options: nil) { (data, _, _, _) in
completionHandler(data, nil)
}
return nil
}
}
Use:
let item = UIDragItem(itemProvider: NSItemProvider.init(object: yourasset))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With