自定义 NSView 拖动目标

Posted

技术标签:

【中文标题】自定义 NSView 拖动目标【英文标题】:Custom NSView Drag Destination 【发布时间】:2011-11-15 04:01:43 【问题描述】:

我正在尝试创建一个简单的 NSView,它允许将 Finder 中的文件夹拖到它上面。文件夹路径是 only 我希望视图接受为可拖动项目的东西。我一直在尝试关注Apple documentation,但到目前为止没有任何效果。到目前为止,我只是试图让视图适用于任何文件类型,但我什至似乎都做不到。到目前为止,这是我所拥有的:

-(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: 方法没有被调用。我错过了一步吗?

【问题讨论】:

有趣。 NSFilenamesPboardType 是为我工作的那个。我在大纲视图中使用acceptDrop:。不过,prepareForDragOperation: 并没有在我的大纲视图中被调用。您是否也尝试过实现performDragOperation:concludeDragOperation: 【参考方案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 &lt;NSDraggingInfo&gt;)sender

根据从该方法返回的NSDragOperation 掩码,如果它在您的类中实现,将调用以下代码:

多个- (NSDragOperation)draggingUpdated:(id &lt;NSDraggingInfo&gt;)sender

根据从该方法返回的NSDragOperation 掩码,将调用prepareForDragOperation:

【讨论】:

所以,这是一个 NSImageView。如果它只是一个 NSView 是否有效? 当然。 (我已将其更改为 NSView 以使其更通用)。在大多数情况下,我使用了NSImageView,因为图像视图的“图像井”样式提供了一个不错的放置目标。 完美。这正是我所需要的。感谢您的帮助。 您可能需要小心[NSBezierPath strokeRect:frame],因为它在屏幕的无效部分周围绘制框架,不一定是当前视图(可能是子视图)。【参考方案2】:

我正在使用 NSURLPboardType 注册从 Finder 中删除的内容(当我将文件或文件夹拖到我的应用程序时,它会以 url 的形式接收它们) 尝试这个。如果它有效,它将解决您的第二个问题:只需检查 URL 是否是接受或拒绝放置的文件夹:

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

【讨论】:

以上是关于自定义 NSView 拖动目标的主要内容,如果未能解决你的问题,请参考以下文章

从 PaintCode 强制自定义 NSView 重绘

macOS:无法在基于自定义 NSView 的类中居中 NSTextField

使用来自 XIB 的自定义 NSView/UIView 子类?

swift 自定义nsview类

将“NSView”绘制到自定义视图 - 如何?我采取了正确的方法吗?

设置 NSView 的背景颜色