Custom NSView Drag Destination

Я пытаюсь создать простой NSView, который позволит перетаскивать папку из Finder. Путь к папке - это единственное, что я хочу, чтобы представление принималось как перетаскиваемый элемент. Я пытался следовать документации Apple, но пока ничего не работает. До сих пор я просто пытался заставить представление работать с любым типом файла, но я даже не могу этого сделать. Вот что я до сих пор:

-(id) initWithFrame:(NSRect)frameRect
{
    if (self = [super initWithFrame:frameRect])
    {
        NSLog(@"getting called");
        [self registerForDraggedTypes:[NSArray arrayWithObjects:NSPasteboardTypeString,
                                       NSPasteboardTypePDF,
                                       NSPasteboardTypeTIFF,
                                       NSPasteboardTypePNG,
                                       NSPasteboardTypeRTF,
                                       NSPasteboardTypeRTFD,
                                       NSPasteboardTypeHTML,
                                       NSPasteboardTypeTabularText,
                                       NSPasteboardTypeFont,
                                       NSPasteboardTypeRuler,
                                       NSPasteboardTypeColor,
                                       NSPasteboardTypeSound,
                                       NSPasteboardTypeMultipleTextSelection,
                                       NSPasteboardTypeFindPanelSearchOptions, nil]];
    }
    return self;
}

-(BOOL) prepareForDragOperation: (id<NSDraggingInfo>) sender
{
    NSLog(@"preparing for drag");

    return YES;
}

Метод initWithFrame: получает вызов, но когда я пытаюсь перетащить его в вид, метод prepareForDragOperation: никогда не будет вызван. Мои вопросы:

  • Что я делаю неправильно? Почему не звонит prepareForDragOperation:?
  • Что мне нужно сделать, чтобы заставить операцию перетаскивания поддерживать только перетаскивание папок?

Обновление

Я обновил метод registerForDraggedTypes: с каждым типом, который мог найти. Теперь он выглядит следующим образом:

[self registerForDraggedTypes:[NSArray arrayWithObjects:NSPasteboardTypeString,
                               NSPasteboardTypePDF,
                               NSPasteboardTypeTIFF,
                               NSPasteboardTypePNG,
                               NSPasteboardTypeRTF,
                               NSPasteboardTypeRTFD,
                               NSPasteboardTypeHTML,
                               NSPasteboardTypeTabularText,
                               NSPasteboardTypeFont,
                               NSPasteboardTypeRuler,
                               NSPasteboardTypeColor,
                               NSPasteboardTypeSound,
                               NSPasteboardTypeMultipleTextSelection,
                               NSPasteboardTypeFindPanelSearchOptions,
                               NSStringPboardType,
                               NSFilenamesPboardType,
                               NSPostScriptPboardType,
                               NSTIFFPboardType,
                               NSRTFPboardType,
                               NSTabularTextPboardType,
                               NSFontPboardType,
                               NSRulerPboardType,
                               NSFileContentsPboardType,
                               NSColorPboardType,
                               NSRTFDPboardType,
                               NSHTMLPboardType,
                               NSURLPboardType,
                               NSPDFPboardType,
                               NSVCardPboardType,
                               NSFilesPromisePboardType,
                               NSMultipleTextSelectionPboardType, nil]];

Я заметил, что метод prepareForDragOperation: не вызывается, когда я перетаскиваю папку в представление. Я пропустил шаг?

Ответ 1

Здесь представлен простой пример перетаскивания, соответствующий этим критериям:

MDDragDropView.h:

@interface MDDragDropView : NSView {
    BOOL        isHighlighted;
}

@property (assign, setter=setHighlighted:) BOOL isHighlighted;

@end

MDDragDropView.m:

@implementation MDDragDropView

@dynamic isHighlighted;

- (void)awakeFromNib {
    NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
}

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
    NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));

    NSPasteboard *pboard = [sender draggingPasteboard];

    if ([[pboard types] containsObject:NSFilenamesPboardType]) {

        NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
        for (NSString *path in paths) {
            NSError *error = nil;
            NSString *utiType = [[NSWorkspace sharedWorkspace]
                                       typeOfFile:path error:&error];
            if (![[NSWorkspace sharedWorkspace] 
                      type:utiType conformsToType:(id)kUTTypeFolder]) {

                [self setHighlighted:NO];
                return NSDragOperationNone;
            }
        }
    }
    [self setHighlighted:YES];
    return NSDragOperationEvery;
}

И остальные методы:

- (void)draggingExited:(id <NSDraggingInfo>)sender {
    [self setHighlighted:NO];
}


- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender  {
    return YES;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
    [self setHighlighted:NO];
    return YES;
}
- (BOOL)isHighlighted {
    return isHighlighted;
}

- (void)setHighlighted:(BOOL)value {
    isHighlighted = value;
    [self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    if (isHighlighted) {
        [NSBezierPath setDefaultLineWidth:6.0];
        [[NSColor keyboardFocusIndicatorColor] set];
        [NSBezierPath strokeRect:self.frame];
    }
}

@end

Причина prepareForDragOperation: не вызывается, так это то, что последовательность назначения перетаскивания следует за точным набором шагов, и если предыдущие шаги не реализованы или реализованы, но возвращают тип "остановить операцию перетаскивания" ответ, более поздние методы никогда не достигаются. (В вашем случае, похоже, вы не реализовали метод draggingEntered:, который должен был бы вернуть что-то, кроме NSDragOperationNone, чтобы продолжить в последовательности).

Прежде чем отправляться prepareForDragOperation:, сначала открывается точка перетаскивания сообщений назначения:

Один - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender.

В зависимости от маски NSDragOperation, возвращаемой с помощью этого метода, будет вызываться следующее, если оно реализовано в вашем классе:

Несколько - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender.

В зависимости от маски NSDragOperation, возвращаемой этим методом, вызывается prepareForDragOperation:.

Ответ 2

Я использую NSURLPboardType для регистрации на вещи, которые удаляются из Finder (когда я перетаскиваю файл или папку в мое приложение, он получает их как URL-адреса) Попробуй это. И если это сработает, это решит вашу вторую проблему: просто проверьте, является ли URL-адрес папкой для принятия или отклонения:

// if item is an NSURL * :
CFURLHasDirectoryPath((CFURLRef)item)
// returns true if item is the URL of a folder.