自定义 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 <NSDraggingInfo>)sender
。
根据从该方法返回的NSDragOperation
掩码,如果它在您的类中实现,将调用以下代码:
多个- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)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 拖动目标的主要内容,如果未能解决你的问题,请参考以下文章
macOS:无法在基于自定义 NSView 的类中居中 NSTextField
使用来自 XIB 的自定义 NSView/UIView 子类?