拖放异步数据获取

时间:2017-07-18 09:00:27

标签: ios drag-and-drop ios11 uidragitem

我正在尝试将Drag and Drop实现到我的共享图像的应用程序中。

我的所有图片都是高性能缩略图(即小尺寸),所以我不能将它们用作我的UIDragItem,至少不是最终图像。

我正在寻找的是一种为我的原始图像提供URL并将其作为UIDragItem发送然后使目标异步获取图像的方法。当图像存储在iCloud中时,这在照片应用程序中完成,所以它必须以某种方式可能,我似乎无法弄清楚如何。

2 个答案:

答案 0 :(得分:5)

事实证明,该解决方案非常简单,在WWDC期间的会话227 Data Delivery with Drag and Drop中进行了描述。

您基本上可以将要拖动的任何对象符合NSItemProviderWriting,然后执行两项操作。

NSItemProviderWriting

  

用于支持基于对象初始化项目提供程序的接口,在提供复制或拖动项目时由源应用程序使用。

第一步

实施writableTypeIdentifiersForItemProvider,让您的接收者知道您提供的对象类型。这是一个具有降低保真度的类型标识符数组(它们在视频中很好地描述了这一点)

第二步

实施繁重的loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress?,当接收者试图加载你提供的对象时,会调用它。

实施例

您可以忽略下面提取的数据的具体细节(我使用的是firebase),但使用原生的URLSession API几乎可以使用相同的方式。

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

答案 1 :(得分:0)

此代码用于拖动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
    }
}

使用:

let item = UIDragItem(itemProvider: NSItemProvider.init(object: yourasset))