NSCollectionView 拖放:大多数委托事件没有被调用

Posted

技术标签:

【中文标题】NSCollectionView 拖放:大多数委托事件没有被调用【英文标题】:NSCollectionView Drag and Dropping: Most Delegate Events Not Getting Called 【发布时间】:2011-08-15 02:42:36 【问题描述】:

我有一个绑定到 NSArrayController 的 NSCollectionView。我想让拖放工作,所以我创建一个委托并实现方法

-(BOOL)collectionView:(NSCollectionView *)collectionView canDragItemsAtIndexes:(NSIndexSet *)indexes withEvent:(NSEvent*)event
-(BOOL)collectionView:(NSCollectionView *)collectionView acceptDrop:(id < NSDraggingInfo >)draggingInfo index:(NSInteger)index dropOperation:(NSCollectionViewDropOperation)dropOperation
-(NSDragOperation)collectionView:(NSCollectionView *)collectionView validateDrop:(id < NSDraggingInfo >)draggingInfo proposedIndex:(NSInteger *)proposedDropIndex dropOperation:(NSCollectionViewDropOperation *)proposedDropOperation
-(NSArray *)collectionView:(NSCollectionView *)collectionView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropURL forDraggedItemsAtIndexes:(NSIndexSet *)indexes

我为这两个 BOOL 方法返回 YES,为 validateDrop: 方法返回 NSDragOperationMove,为 namesOfPromisedFilesDroppedAtDestination: 方法返回一个空数组。我还有一个 NSLog 语句作为每个方法的第一行,所以我可以看到它们何时被调用。

现在,唯一被调用的方法是 canDragItemsAtIndexes:(我返回 YES)。我看到它被调用了,但是任何进一步的拖动只会修改选择。其余的永远不会被调用。

如果我让 NSCollectionView 不支持选择,那么甚至不会调用该方法。

我确定我遗漏了一些非常明显的东西,但我无法弄清楚它是什么。有没有人使用 NSCollectionViews 进行拖放并且可以阐明一些问题?

【问题讨论】:

【参考方案1】:

我认为您错过了将拖动内容写入粘贴板的部分。 要支持拖放,您必须执行以下步骤:

    确定是否可以拖动拖动源 如果是YES,将内容写入粘贴板 验证并接受放置目标中的项目

写入粘贴板应在- collectionView:writeItemsAtIndexes:toPasteboard:

中实现

您还必须使用- registerForDraggedTypes: 注册您拖动的类型

一些示例代码: http://developer.apple.com/library/mac/#samplecode/IconCollection/Introduction/Intro.html

【讨论】:

如果 Apple 将其包含在委托协议文档的“拖放支持”部分中,那就太好了。谢谢。 还要确保您的收藏视图是“可选择的” - 否则您将无法开始拖动项目【参考方案2】:

这段代码包含了将图像从一个 NSCollectionView 拖到另一个所需的一切。弄清楚这一点并不是很明显。为源集合视图检查 Selectable 并为 dataSource 和委托出口连接,但我不需要 registerForDraggedTypes。

class Window:NSWindow, NSComboBoxDelegate, NSTextFieldDelegate, NSDatePickerCellDelegate, NSTableViewDataSource, NSTableViewDelegate, MKMapViewDelegate, NSCollectionViewDataSource, NSCollectionViewDelegate, NSCollectionViewDelegateFlowLayout, NSTabViewDelegate, NSMenuDelegate, NSDraggingDestination  

    func collectionView(collectionView: NSCollectionView, writeItemsAtIndexPaths indexPaths: Set<NSIndexPath>, toPasteboard pasteboard: NSPasteboard) -> Bool 
    let index = indexPaths.first!.item
    let url = webImageURLs[index]   // array of string URLs that parallels the collection view.
    NSPasteboard.generalPasteboard().clearContents()
    NSPasteboard.generalPasteboard().declareTypes([kUTTypeText as String, kUTTypeData as String], owner: nil)
    NSPasteboard.generalPasteboard().setString(url, forType: (kUTTypeText as String))
    NSPasteboard.generalPasteboard().setData(webImageData[index], forType: (kUTTypeData as String))
    return true


// Provide small version of image being dragged to accompany mouse cursor.
func collectionView(collectionView: NSCollectionView, draggingImageForItemsAtIndexPaths indexPaths: Set<NSIndexPath>, withEvent event: NSEvent, offset dragImageOffset: NSPointPointer) -> NSImage 
    let item = collectionView.itemAtIndex(indexPaths.first!.item)
    return (item?.imageView?.image)!.resizeImage(20, height: 20)


// Image is dropped on destination NSCollectionView.
func collectionView(collectionView: NSCollectionView, draggingSession session: NSDraggingSession, endedAtPoint screenPoint: NSPoint, dragOperation operation: NSDragOperation) 
    let pasteboardItem = NSPasteboard.generalPasteboard().pasteboardItems![0]
    let urlString = pasteboardItem.stringForType((kUTTypeText as String))
    let imageData = pasteboardItem.dataForType((kUTTypeData as String))

    // destinationImages is the data source for the destination collectionView. destinationImageURLs is used to keep track of the text urls.
    if urlString != nil 
        destinationImageURLs.insert(urlString!, atIndex: 0)
        destinationImages.insert(NSImage(data: imageData!)!, atIndex: 0)
        destinationCollectionView.reloadData()
        let selectionRect = self.favoritesCollectionView.frameForItemAtIndex(0)
        destinationCollectionView.scrollRectToVisible(selectionRect)
    


extension NSImage 
    func resizeImage(width: CGFloat, height: CGFloat) -> NSImage 
        let img = NSImage(size: CGSizeMake(width, height))
        img.lockFocus()
        let ctx = NSGraphicsContext.currentContext()
        ctx?.imageInterpolation = .High
        drawInRect(NSRect(x: 0, y: 0, width: width, height: height), fromRect: NSRect(x: 0, y: 0, width: size.width, height: size.height), operation: .CompositeCopy, fraction: 1)
        img.unlockFocus()

        return img
    
 

【讨论】:

我无法收到回调draggingImageForItemsAtIndexPaths,您是否设置了其他内容? 我在答案中的代码中添加了类定义。查看我正在实施的所有协议,看看您是否缺少与拖动或 NSCollectionView 相关的任何协议。 我刚刚在头文件中找到了一行。因为我正在使用委托方法pasteboardWriterForItemAtIndexPath。文档说If this method is implemented, then -collectionView:writeItemsAtIndexPaths:toPasteboard: and -collectionView:draggingImageForItemsAtIndexPaths:withEvent:offset: will not be called. 我没有使用 pasteboardWriterForItemAtIndexPath。

以上是关于NSCollectionView 拖放:大多数委托事件没有被调用的主要内容,如果未能解决你的问题,请参考以下文章

透明 NSCollectionView 背景

Qt:将拖放委托给孩子的最佳方式

通过拖动委托向上或向下拖动一行来重新排列表格中的部分(iOS 11)

NSCollectionView 斯威夫特

带有网络图像的 NSCollectionView

GCD 块不更新 NSCollectionView