拖放异步数据获取
Drag and Drop asynchronous data fetching
我正在尝试在共享图像的应用程序中实现拖放功能。
我所有的图像都是高性能缩略图(即小尺寸),所以我不能将它们用作我的 UIDragItem
,至少不能用作最终图像。
我正在寻找的是一种为我的原始图像提供 URL 并将其作为 UIDragItem
发送,然后让目的地异步获取图像的方法。当图像存储在 iCloud 中时,这是在照片应用程序中完成的,所以它一定是可能的,我似乎无法弄清楚如何。
原来解决方案非常简单,在本次 WWDC 的第 227 节 Data Delivery with Drag and Drop 中进行了描述。
你基本上让你想要拖动的任何对象符合 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.
第一步
实施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]
}
}
此代码用于拖动 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))
我正在尝试在共享图像的应用程序中实现拖放功能。
我所有的图像都是高性能缩略图(即小尺寸),所以我不能将它们用作我的 UIDragItem
,至少不能用作最终图像。
我正在寻找的是一种为我的原始图像提供 URL 并将其作为 UIDragItem
发送,然后让目的地异步获取图像的方法。当图像存储在 iCloud 中时,这是在照片应用程序中完成的,所以它一定是可能的,我似乎无法弄清楚如何。
原来解决方案非常简单,在本次 WWDC 的第 227 节 Data Delivery with Drag and Drop 中进行了描述。
你基本上让你想要拖动的任何对象符合 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.
第一步
实施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]
}
}
此代码用于拖动 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))