Drag and Drop асинхронная выборка данных
Я пытаюсь внедрить Drag and Drop в свое приложение, которое делится изображениями.
Все мои изображения являются высокопроизводительными миниатюрами (т.е. небольшого размера), поэтому я не могу использовать их как свои UIDragItem
По крайней мере, не как окончательное изображение.
То, что я ищу, это способ предоставить URL для моего исходного изображения и отправить его как UIDragItem
и затем получатель получит изображение асинхронно. Это делается в приложении "Фотографии", когда изображение хранится в iCloud, поэтому это должно быть как-то возможно, я просто не могу понять, как это сделать.
1 ответ
Оказывается, решение довольно простое и описано в сеансе 227 " Доставка данных с перетаскиванием" во время этой WWDC.
Вы в основном делаете любой объект, который хотите перетащить, соответствующим NSItemProviderWriting, а затем реализуете две вещи.
Интерфейс для поддержки инициализации поставщика элемента на основе объекта, используемого исходным приложением при предоставлении скопированных или перетаскиваемых элементов.
Первый шаг
Воплощать в жизнь writableTypeIdentifiersForItemProvider
что даст вашему получателю представление о том, какой тип объекта вы предоставляете. Это массив идентификаторов типов с уменьшающейся точностью (они хорошо это описывают в видео)
Шаг второй
Воплощать в жизнь loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress?
который выполняет тяжелую работу, вызывается, когда получатель пытается загрузить объект, который вы предоставляете.
пример
Вы можете не обращать внимания на особенности извлечения данных ниже (я использую firebase), но использование нативного API URLSession будет работать точно так же.
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))