Drag and Drop Asynchronous Data Using Drag and Drop

I am trying to implement Drag and Drop in my application that shares images.

All my images are thumbnails with high performance (i.e. small sizes), so I can not use them as mine UIDragItem, at least not as the final image.

What I'm looking for is a way to provide the URL for my source image and send it as UIDragItem, and then assign to receive the image asynchronously. This is done in the Photos application, when the image is stored in iCloud, so it should somehow be possible, I just can’t figure out how to do it.

+4
source share
1 answer

It turns out that the solution is quite simple and is described in session 227 “Drag and Drop Data Delivery” during this WWDC.

Basically you make any object you want to drag according to NSItemProviderWriting , and then implement two things.

NSItemProviderWriting :

An interface to support initialization of a product supplier based on an object used by the source application when providing copied or dragged items.

Step one

Implement writableTypeIdentifiersForItemProviderone that will give your receiver an idea of ​​what type of facility you are providing. This is an array of type identifiers with decreasing precision (they describe it well in the video)

Second step

loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress?, , , , .

( firebase), API URL- .

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
  }
}

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]
  }
}
+4

Source: https://habr.com/ru/post/1681676/


All Articles