如何在NSCollectionView中创建用于拖放的NSPasteboardWriting

JFS*_*JFS 5 drag-and-drop nscollectionview nspasteboard swift

我有一个部分的集合视图,并希望实现拖放以允许重新排序项目.该CollectionViewItem有几个textviews显示特性形成我的参数对象.阅读我需要实现的文档NSCollectionView delegate:

func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
    let parameter = parameterForIndexPath(indexPath: indexPath)
    return parameter // throws an error "Cannot convert return expression of type 'Parameter' to return type 'NSPasteboardWriting?'"
}
Run Code Online (Sandbox Code Playgroud)

我没有找到任何可以理解的描述NSPasteboardWriting对象性质的信息.所以,我不知道如何继续...什么是NSPasteboardWriting对象,我需要在粘贴板中写什么?

谢谢!

jef*_*f-h 6

免责声明:我一直在努力寻找任何解释这一点的方式,这对我来说是有意义的,特别是对于Swift而言,并且不得不将以下内容拼凑成一个很大的难度.如果你知道的更好,请告诉我,我会纠正它!

"pasteboardwriter"方法(例如您的问题中的方法)必须返回可以被拖动的项目可识别的内容,可以将其写入粘贴板.然后拖放方法绕过此粘贴板项目.

我见过的大多数例子只是使用对象的字符串表示.你需要这个,以便在acceptDrop方法中你可以让你的手回到原始对象(被拖动的项目).然后,您可以重新订购该项目的位置,或者您需要采取的任何操作.

拖放涉及四个主要步骤.我目前正在使用源列表视图执行此操作,因此我将使用该示例而不是您的集合视图.

  1. viewDidLoad() 寄存器中的SOURCELIST以接受删除的对象.通过告诉它应该接受哪种粘贴板类型来做到这一点.

    // Register for the dropped object types we can accept. sourceList.register(forDraggedTypes: [REORDER_SOURCELIST_PASTEBOARD_TYPE])

    这里我使用的是自定义类型,REORDER_SOURCELIST_PASTEBOARD_TYPE我将其定义为常量,如下所示:

       `let REORDER_SOURCELIST_PASTEBOARD_TYPE = "com.yourdomain.sourcelist.item"`
    
    Run Code Online (Sandbox Code Playgroud)

    ...值是您的应用程序独有的东西,即yourdomain应该更改为您的应用程序特定的内容,例如com.myapp.sourcelist.item.

    我在任何类之外定义它(因此它可以从几个类访问),如下所示:

       import Cocoa
    
       let REORDER_SOURCELIST_PASTEBOARD_TYPE = "com.yourdomain.sourcelist.item"`
    
       class Something {
          // ...etc...
    
    Run Code Online (Sandbox Code Playgroud)
  2. 实现视图的pasteboardWriterForItem方法.根据您使用的视图(即源列表,集合视图或其他),这会略有不同.对于源列表,它看起来像这样:

      // Return a pasteboard writer if this outlineview's item should be able to 
      // drag-and-drop.
      func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? {
        let pbItem = NSPasteboardItem()
    
        // See if the item is of the draggable kind. If so, set the pasteboard item.
        if let draggableThing = ((item as? NSTreeNode)?.representedObject) as? DraggableThing {
              pbItem.setString(draggableThing.uuid, forType: REORDER_SOURCELIST_PASTEBOARD_TYPE)
              return pbItem;
        }
    
        return nil
      }
    
    Run Code Online (Sandbox Code Playgroud)

    其中最值得注意的部分draggableThing.uuid是一个字符串,它可以通过其粘贴板唯一地标识拖动的对象.

  3. 弄清楚您拖动的项目是否可以在给定索引处的建议项目上删除,如果是,则返回应该是的丢弃类型.

    func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
        // Get the pasteboard types that this dragging item can offer. If none
        // then bail out.
        guard let draggingTypes = info.draggingPasteboard().types else {
            return []
        }
    
        if draggingTypes.contains(REORDER_SOURCELIST_PASTEBOARD_TYPE) {
            if index >= 0 && item != nil {
                return .move
            }
          }
    
          return []
        }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 处理drop事件.执行诸如将拖动的项目移动到数据模型中的新位置并重新加载视图或移动视图中的行等操作.

    func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
      let pasteboard = info.draggingPasteboard()
    
      let uuid = pasteboard.string(forType: REORDER_SOURCELIST_PASTEBOARD_TYPE)
    
      // Go through each of the tree nodes, to see if this uuid is one of them.
      var sourceNode: NSTreeNode?
      if let item = item as? NSTreeNode, item.children != nil {
        for node in item.children! {
            if let collection = node.representedObject as? Collection {
                if collection.uuid == uuid {
                    sourceNode = node
                }
            }
        }
      }
    
      if sourceNode == nil {
        return false
      }
    
      // Reorder the items.
      let indexArr: [Int] = [1, index]
      let toIndexPath = NSIndexPath(indexes: indexArr, length: 2)
      treeController.move(sourceNode!, to: toIndexPath as IndexPath)
    
      return true
    }
    
    Run Code Online (Sandbox Code Playgroud)

旁白:Cocoa要求我们使用粘贴板项目进行拖放似乎对我来说是非常不必要的 - 为什么它不能简单地传递我不知道的原始(即拖动)对象!显然有些drags来自应用程序之外,但是对于那些源自它的人来说,确定传递对象可以节省所有与粘贴板一起跳跃的跳跃.