我在通过拖放会话(使用UICollectionView
's Drag&Drop feature)加载pdf文件时遇到问题。
在collectionView(_:performDropWith:coordinator)
内,我想将放置的项目加载到后台线程中:
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
// I'm dropping a valid pdf file from the Files app in iOS.
// I'm using performBackgroundTask because I want to save stuff to the database
appDelegate.persistentContainer.performBackgroundTask({ (privateContext) in
for item in coordinator.items {
// This correctly returns true
if item.dragItem.itemProvider.canLoadObject(ofClass: MyPDFDocument.self) {
item.dragItem.itemProvider.loadObject(ofClass: MyPDFDocument.self) { (pdfItem, error) in
// This is not called
}
}
}
})
}
final class MyPDFDocument: PDFDocument, NSItemProviderReading {
public static var readableTypeIdentifiersForItemProvider: [String] {
return [kUTTypePDF as String]
}
public static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> ComicBookPDFDocument {
return MyPDFDocument(data: data)!
}
}
但是,它不起作用。应该loadObject(ofClass:)
调用块,而根本不调用它。在主线程上工作得很好。
问题是,我无法将performBackgroundTask
块放在loadObject(ofClass:)
内(然后放置的对象会完美加载),因为如果放置多个pdf文件,则在保存上下文时会导致合并错误(因为后台任务针对每个删除的文件同时运行。
有什么想法吗?是否不允许从另一个线程中加载对象?
答案 0 :(得分:0)
查看代码已经很长时间了,但是我希望这会有所帮助:
对我来说,解决方案是使用方法loadObjects(ofClass:completion:)
。这将异步获取所需文件的所有对象。 documentation中未说明。但是,如果您按cmd单击该方法并转到其声明,则会出现以下注释:
/* A convenience method that can be used only during the UIDropInteractionDelegate's
* implementation of `-dropInteraction:performDrop:`.
* Asynchronously instantiates objects of the provided class for each
* drag item that can do so. The completion handler is called on the
* main queue, with an array of all objects that were created, in the
* same order as `items`.
* The progress returned is an aggregate of the progress for all objects
* that are loaded.
*/
func loadObjects(ofClass aClass: NSItemProviderReading.Type, completion: @escaping ([NSItemProviderReading]) -> Void) -> Progress
实例化对象后,在主队列上调用完成处理程序。从这里,您可以调度到后台队列,并对文件进行任何操作。
我抓取了最终得到的代码并对其进行了简化:
// ValidComicBookFile is my NSItemProviderReading type that accepts multiple file types, like archive and pdf
coordinator.session.loadObjects(ofClass: ValidComicBookFile.self) { (importedObjects) in
guard let validComicBookFiles = importedObjects as? [ValidComicBookFile] else {
// Handle errors
}
DispatchQueue.global(qos: .userInitiated).async {
// ...
for (index, file) in validComicBookFiles.enumerated() {
switch file.fileType {
case .archive:
// Unpack it and stuff
case .pdf:
// For example:
if let pdfDoc = PDFDocument(data: file.data) {
pdfs.append(pdfDoc) // Append it to an array I created earlier
}
case .none:
break
}
}
// ...
}
}
一个注意事项:我最终并不需要保存到数据库,但是这样,(希望)这应该不是问题。