【问题标题】:How to create NSPasteboardWriting for Drag and Drop in NSCollectionView如何在 NSCollectionView 中为拖放创建 NSPasteboardWriting
【发布时间】:2017-07-08 00:06:17
【问题描述】:

我有一个单节集合视图,并希望实现拖放以允许对项目进行重新排序。 CollectionViewItem 有几个 textviews 显示我的 Parameter 对象的属性。阅读文档我需要实现NSCollectionViewdelegate

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?'"
}

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

谢谢!

【问题讨论】:

    标签: swift drag-and-drop nscollectionview nspasteboard


    【解决方案1】:

    免责声明:我一直在努力寻找任何对我来说有意义的方式来解释这一点,尤其是对于 Swift,我不得不将以下内容拼凑起来非常困难。如果你知道的更好,请告诉我,我会改正的!

    “pasteboardwriter”方法(例如您问题中的方法)必须返回可识别的内容,以便将要拖动的项目写入粘贴板。然后拖放方法会绕过此粘贴板项。

    我见过的大多数示例都只是使用对象的字符串表示。您需要这个,以便在 acceptDrop 方法中您可以将手重新放在原始对象(被拖动的项目)上。然后您可以重新排序该项目的位置,或者您需要对其采取的任何操作。

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

    1. viewDidLoad() 注册源列表视图以接受丢弃的对象。通过告诉它应该接受哪种粘贴板类型来做到这一点。

      // 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"`
      

      ...如果值是您的应用程序独有的值,即yourdomain,应更改为特定于您的应用程序的值,例如com.myapp.sourcelist.item

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

         import Cocoa
      
         let REORDER_SOURCELIST_PASTEBOARD_TYPE = "com.yourdomain.sourcelist.item"`
      
         class Something {
            // ...etc...
      
    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
        }
      

      其中最值得注意的部分是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 []
          }
      
    4. 处理放置事件。执行诸如将拖动的项目移动到数据模型中的新位置并重新加载视图或移动视图中的行等操作。

      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
      }
      

    除此之外:我们使用粘贴板项目进行拖放的 Cocoa 指令对我来说似乎非常不必要 --- 为什么它不能简单地传递我不知道的原始(即拖动)对象!显然,有些拖拽源自应用程序外部,但对于那些源自应用程序内部的拖拽,肯定会传递对象将节省所有使用粘贴板的箍跳。

    【讨论】:

    • 从痛苦中恢复了很长时间后,我开始尝试解决我仍然无法正常工作的拖放问题。我不明白你对let REORDER_SOURCELIST_PASTEBOARD_TYPE = "com.yourdomain.sourcelist.item" 的意思。什么是“您的域”,我是否需要将其更改为适合我的应用程序的内容? “我在课堂之外定义的......”是什么意思?课外是什么意思?谢谢
    • REORDER_SOURCELIST_PASTEBOARD_TYPE 需要唯一标识一种自定义类型的粘贴板“东西”,在这种情况下,它是您发明的用于携带拖放信息的粘贴板项目。所以是的,你是对的——把它改成你的应用独有的东西。
    • “我的课外”是指应用程序范围内的常量。对困惑感到抱歉。我已经更新了我的答案,希望能澄清这一点。
    • collectionView.registerForDraggedTypes - 在 Swift 4 中重命名
    【解决方案2】:

    NSPasteboardWriting 协议提供了NSPasteboard(我猜从技术上讲是任何人)可以用来生成对象的不同表示的方法,以便在粘贴板周围传输,这是一个较旧的 Apple 概念用于复制/粘贴(因此 Pasteboard),并且在某些情况下显然可以拖放。

    看来,协议的自定义实现基本上需要实现以下方法:

    另一个答案提供了此协议的简单实现,以便在单个应用程序中使用。


    但实际上呢?

    Cocoa 框架类 NSString、NSAttributedString、NSURL、NSColor、NSSound、NSImage 和 NSPasteboardItem 实现了这个协议。

    因此,如果您有一个可以完全表示为 URL(或字符串、颜色、声音或图像等)的可拖动项目,只需将您拥有的 URL 转换为 NSPasteboardWriting?

    func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
        let url: URL? = someCodeToGetTheURL(for: indexPath) // or NSURL
        return url as NSPasteboardWriting?
    }
    

    如果你有一个复杂的类型,这没有帮助,但如果你有一个图像的集合视图或其他一些基本项目,我希望它会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      相关资源
      最近更新 更多