macOS 实现 drag and drop 拖放很复杂,因为苹果的开发者中心把很多 demo 代码都不知道放到哪里去了,你用苹果开发者网站找不到 Sample Code,用 Xcode 帮助也没法找到(补充:sample code 在 https://developer.apple.com/library/archive/navigation/#section=Resource%20Types&topic=Sample%20Code )。而通过搜索引擎找的话,各种例子不是老旧就是没讲对。
其实 drag and drop 很简单。有两个概念:DraggingSource 和 DraggingDestination。你可以把拖放理解两个容器之间的交互,你要将某物从A拖到B然后放下。
最重要的一点,不管实现什么接口什么方法,你必须告诉系统什么时候拖放开始了。我看了很多文章,都没有重点提到这一点。也就是说,不管你实现什么接口都没有用,你必须主动告诉系统拖放已经开始了。所以,在你的代码中明确地告诉系统这一点,一般是鼠标按住的时候。
当你决定拖放已经开始了,就调用这个方法:
// NSView 的方法,当你决定 drag-n-drop 可以开始的时候,调用此方法 - (NSDraggingSession *)beginDraggingSessionWithItems:(NSArray*)items event:(NSEvent *)event source:(id )source;
调用这个方法之后,系统会自动地更新拖动过程的示意图的位置。当然,你要告诉系统示意图的开始位置和图片,通过上面的方法中的参数。另外,drag-n-drop 通过剪贴板在源和目的之间进行通信,所以剪贴板充当通信信道。下面的代码配置了通信信道和示意图:
// NSPasteboardItem 用于在 drag-n-drap 的双方之间进行通信 NSPasteboardItem *pbItem = [NSPasteboardItem new]; // 你可以直接传输数据, type 可自定义. [pbItem setString:@"Transition" forType:NSPasteboardTypeString]; // 或者指定传输的数据由 NSPasteboardItemDataProvider 提供。 //[pbItem setDataProvider:self forTypes:@[NSPasteboardTypeString]]; // NSDraggingItem 用于显示 drag-n-drop 过程的示意图 NSDraggingItem *dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pbItem]; NSImage *img = [NSImage imageNamed:NSImageNameHomeTemplate]; NSRect frame = NSMakeRect(0, 0, img.size.width, img.size.height); // draggingFrame 用于指定示意图的初始位置(在当前 NSView 中),contents 是示意图(NSImage) [dragItem setDraggingFrame:frame contents:img];
这认为用网络通信模型来理解会更好,网络和通信是一个广义的概念,可以解释很多东西,我之前有文章介绍过:http://www.ideawu.net/blog/archives/528.html。
弄明白了上面的之后,你再查看这几个接口就懂了:NSDraggingSource, NSDraggingDestination, NSPasteboardItemDataProvider