You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用NSOutlineView的pasteboardWriterForItem:实现多条目写入?

解决NSOutlineView多条目拖放的标准实现

1. 定义自定义粘贴板类型与数据模型

首先定义唯一的粘贴板类型标识,再让你的数据模型类遵循NSPasteboardWriting和NSPasteboardReading协议,实现序列化/反序列化逻辑:

private let OutlineItemPasteboardType = NSPasteboard.PasteboardType("com.yourcompany.outlineitem")

class OutlineItem: NSObject, NSPasteboardWriting, NSPasteboardReading {
    var title: String
    var children: [OutlineItem] = []
    
    init(title: String) {
        self.title = title
        super.init()
    }
    
    // MARK: NSPasteboardWriting
    func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
        return [OutlineItemPasteboardType]
    }
    
    func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
        guard type == OutlineItemPasteboardType else { return nil }
        // 序列化数据,复杂结构可改用JSON/PropertyList
        return ["title": title]
    }
    
    // MARK: NSPasteboardReading
    static func readableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
        return [OutlineItemPasteboardType]
    }
    
    static func readingOptions(forType type: NSPasteboard.PasteboardType, pasteboard: NSPasteboard) -> NSPasteboard.ReadingOptions {
        return .asPropertyList
    }
    
    required init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) {
        guard type == OutlineItemPasteboardType,
              let dict = propertyList as? [String: String],
              let title = dict["title"] else {
            return nil
        }
        self.title = title
        super.init()
    }
}

2. 实现数据源的粘贴板Writer方法

让outlineView(_:pasteboardWriterForItem:)返回对应条目的writer实例,NSOutlineView会自动将所有选中条目的writer添加到粘贴板,不会出现覆盖:

func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? {
    guard let outlineItem = item as? OutlineItem else { return nil }
    return outlineItem
}

3. 在接受拖放时读取所有条目

在outlineView(_:acceptDrop:item:childIndex:)中批量读取粘贴板内的所有条目,完成插入逻辑:

func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
    guard let pasteboard = info.draggingPasteboard,
          let items = pasteboard.readObjects(forClasses: [OutlineItem.self], options: nil) as? [OutlineItem] else {
        return false
    }
    
    // 插入到目标位置(需创建副本避免引用冲突)
    let parentItem = item as? OutlineItem ?? yourRootItem
    var insertionIndex = index
    for droppedItem in items {
        let newItem = OutlineItem(title: droppedItem.title)
        parentItem.children.insert(newItem, at: insertionIndex)
        insertionIndex += 1
    }
    
    outlineView.reloadData()
    return true
}

4. 配置拖放操作权限(可选)

实现代理方法设置允许的拖放操作类型:

func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, sourceOperationMaskFor context: NSDraggingContext) -> NSDragOperation {
    return .copy // 或 .move,根据业务需求调整
}

若之前出现粘贴板仅保留最后一个条目的问题,通常是因为数据模型未正确遵循粘贴板协议,或序列化逻辑导致同一类型数据被覆盖,按上述标准实现即可解决。

内容的提问来源于stack exchange,提问作者blackirishman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 11:15:30