Ответ 1
После перехода на NSPasteboard и перехода через приложение, я понял, что Photos.app передает "обещанные файлы" в картотеке и нашел этот поток в списке рассылки Apple с некоторыми ответами: http://prod.lists.apple.com/archives/cocoa-dev/2015/Apr/msg00448.html
Вот как я, наконец, решил это, в классе, который обрабатывает перетаскивание файлов в документ. Класс является контроллером представления, который обрабатывает обычные методы перетаскивания, потому что он в цепочке ответчиков.
Метод удобства обнаруживает, что отправитель перетаскивания имеет любой связанный с файлом контент:
- (BOOL)hasFileURLOrPromisedFileURLWithDraggingInfo:(id <NSDraggingInfo>)sender
{
NSArray *relevantTypes = @[@"com.apple.pasteboard.promised-file-url", @"public.file-url"];
for(NSPasteboardItem *item in [[sender draggingPasteboard] pasteboardItems])
{
if ([item availableTypeFromArray:relevantTypes] != nil)
{
return YES;
}
}
return NO;
}
У меня также есть способ извлечь URL-адрес в случае, если он не является "обещанным файлом":
- (NSURL *)fileURLWithDraggingInfo:(id <NSDraggingInfo>)sender
{
NSPasteboard *pasteboard = [sender draggingPasteboard];
NSDictionary *options = [NSDictionary dictionaryWithObject:@YES forKey:NSPasteboardURLReadingFileURLsOnlyKey];
NSArray *results = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:options];
return [results lastObject];
}
Вот, наконец, метод, используемый для обработки капли. Это не совсем точно мой код, поскольку я упростил внутреннюю обработку перетаскивания в удобные методы, которые позволяют мне скрывать детали, специфичные для приложения. У меня также есть специальный класс для обработки событий файловой системы FileSystemEventCenter
слева как упражнение для читателя. Кроме того, в представленном здесь примере я обрабатываю только перетаскивание файла один. Вам придется адаптировать эти части к вашему собственному случаю.
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
if ([self hasFileURLOrPromisedFileURLWithDraggingInfo:sender])
{
[self updateAppearanceWithDraggingInfo:sender];
return NSDragOperationCopy;
}
else
{
return NSDragOperationNone;
}
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{
return [self draggingEntered:sender];
}
- (void)draggingExited:(id <NSDraggingInfo>)sender
{
[self updateAppearanceWithDraggingInfo:nil];
}
- (void)draggingEnded:(id <NSDraggingInfo>)sender
{
[self updateAppearanceWithDraggingInfo:nil];
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
{
return [self hasFileURLOrPromisedFileURLWithDraggingInfo:sender];
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
// promised URL
NSPasteboard *pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:NSFilesPromisePboardType])
{
// promised files have to be created in a specific directory
NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
if ([[NSFileManager defaultManager] createDirectoryAtPath:tempPath withIntermediateDirectories:NO attributes:nil error:NULL] == NO)
{
return NO;
}
// the files will be created later: we keep an eye on that using filesystem events
// `FileSystemEventCenter` is a wrapper around FSEvent
NSArray *filenames = [sender namesOfPromisedFilesDroppedAtDestination:[NSURL fileURLWithPath:tempPath]];
DLog(@"file names: %@", filenames);
if (filenames.count > 0)
{
self.promisedFileNames = filenames;
self.directoryForPromisedFiles = tempPath.stringByStandardizingPath;
self.targetForPromisedFiles = [self dropTargetForDraggingInfo:sender];
[[FileSystemEventCenter defaultCenter] addObserver:self selector:@selector(promisedFilesUpdated:) path:tempPath];
return YES;
}
else
{
return NO;
}
}
// URL already here
NSURL *fileURL = [self fileURLWithDraggingInfo:sender];
if (fileURL)
{
[self insertURL:fileURL target:[self dropTargetForDraggingInfo:sender]];
return YES;
}
else
{
return NO;
}
}
- (void)promisedFilesUpdated:(FDFileSystemEvent *)event
{
dispatch_async(dispatch_get_main_queue(),^
{
if (self.directoryForPromisedFiles == nil)
{
return;
}
NSString *eventPath = event.path.stringByStandardizingPath;
if ([eventPath hasSuffix:self.directoryForPromisedFiles] == NO)
{
[[FileSystemEventCenter defaultCenter] removeObserver:self path:self.directoryForPromisedFiles];
self.directoryForPromisedFiles = nil;
self.promisedFileNames = nil;
self.targetForPromisedFiles = nil;
return;
}
for (NSString *fileName in self.promisedFileNames)
{
NSURL *fileURL = [NSURL fileURLWithPath:[self.directoryForPromisedFiles stringByAppendingPathComponent:fileName]];
if ([[NSFileManager defaultManager] fileExistsAtPath:fileURL.path])
{
[self insertURL:fileURL target:[self dropTargetForDraggingInfo:sender]];
[[FileSystemEventCenter defaultCenter] removeObserver:self path:self.directoryForPromisedFiles];
self.directoryForPromisedFiles = nil;
self.promisedFileNames = nil;
self.targetForPromisedFiles = nil;
return;
}
}
});
}