简体   繁体   中英

How to set the Pasteboard Property List for a custom type - NSPasteboard / Swift

I'm working on a Cocoa application to drag and drop files between two NSTableViews. Rather than using just the URL, I want to use a custom struct so I can have access to more data if needed and not make constant calls to the FileManager.

I believe that I need to implement conform my custom Pasteboard Utility to NSPasteboardReading so I can properly digest the data on the receiving table.

I'm unsure of exactly what's needed to set the init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) function as required when dealing with the custom struct that I'm using in the Pasteboard.

Frankly, I'm unsure of how to use the property list in this situation as I've generally only used it in setting global application plists in the past.

Sadly, there's not a whole lot of resources here. Most examples I've seen generally reference JSON objects for the Property list. I'm unsure if I need to extract the data from the custom Type into an array of Data or String types.

Any guidance here on the implementation or even better guidance on what's possible with Property Lists would be most appreciated!

Custom Struct passed to the PasteBoard:

struct TidiFile {
    var url : URL?
    var createdDateAttribute : Date?
    var modifiedDateAttribute : Date?
    var fileSizeAttribute: Int?


    //setting for a nil init so this can return nil values in case of failure to set attributes
    init( url : URL? = nil,
        createdDateAttribute : Date? = nil,
        modifiedDateAttribute : Date? = nil,
        fileSizeAttribute: Int? = nil) {
        self.url = url
        self.createdDateAttribute = createdDateAttribute
        self.modifiedDateAttribute = modifiedDateAttribute
        self.fileSizeAttribute = fileSizeAttribute
    }
}

Table View Controller: Where I write the item to the Pasteboard

    func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
        return PasteboardWriter(tidiFile: tableSourceTidiFileArray[row], at: row)
    }

Table View Controller: Where I want to accept the drop and move the file

func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {

            let pasteboard = info.draggingPasteboard
            let pasteboardItems = pasteboard.pasteboardItems


    }

Custom Pasteboard Utility:

import Foundation
import Cocoa

class PasteboardWriter: NSObject, NSPasteboardWriting, NSPasteboardReading {
    required init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) {
        // Need to implement
    }

    var tidiFile : TidiFile
    var index: Int


    init(tidiFile : TidiFile, at index: Int) {
        self.tidiFile = tidiFile
        self.index = index
    }

    func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
        return [.tableViewIndex, .tidiFile]
    }

    func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
        switch type {
        case .tidiFile:
            return tidiFile
        case .tableViewIndex:
            return index
        default:
            return nil
        }
    }


    static func readableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
        return [.tableViewIndex, .tidiFile]
    }

}

extension NSPasteboard.PasteboardType {
    static let tableViewIndex = NSPasteboard.PasteboardType("com.bradzellman.tableViewIndex")
    static let tidiFile = NSPasteboard.PasteboardType("com.bradzellman.tidiFile")
}


extension NSPasteboardItem {
    open func integer(forType type: NSPasteboard.PasteboardType) -> Int? {
        guard let data = data(forType: type) else { return nil }
        let plist = try? PropertyListSerialization.propertyList(
            from: data,
            options: .mutableContainers,
            format: nil)
        return plist as? Int
    }
}

First of all to be able to drag&drop a custom object this object must be a sub class of NSObject .

This is a quick&dirty implementation with non-optional types. The data is serialized to and from Property List with Codable . The protocol methods init(from decoder and encode(to encoder are synthesized.

In init?(pasteboardPropertyList you have to decode an instance and create a new one with the standard initializer.

final class TidiFile : NSObject, Codable {

    var url : URL
    var createdDateAttribute : Date
    var modifiedDateAttribute : Date
    var fileSizeAttribute: Int

    init(url: URL, createdDateAttribute: Date, modifiedDateAttribute: Date, fileSizeAttribute: Int) {
        self.url = url
        self.createdDateAttribute = createdDateAttribute
        self.modifiedDateAttribute = modifiedDateAttribute
        self.fileSizeAttribute = fileSizeAttribute
    }

    convenience init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) {
        guard let data = propertyList as? Data,
            let tidi = try? PropertyListDecoder().decode(TidiFile.self, from: data) else { return nil }
        self.init(url: tidi.url, createdDateAttribute: tidi.createdDateAttribute, modifiedDateAttribute: tidi.modifiedDateAttribute, fileSizeAttribute: tidi.fileSizeAttribute)
    }

}

extension TidiFile : NSPasteboardWriting, NSPasteboardReading
{

    public func writingOptions(forType type: NSPasteboard.PasteboardType, pasteboard: NSPasteboard) -> NSPasteboard.WritingOptions {
        return .promised
    }

    public func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
        return [.tidiFile]
    }

    public func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
        if type == .tidiFile {
            return try? PropertyListEncoder().encode(self)
        }
        return nil
    }

    public static func readableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
        return [.tidiFile]
    }

    public static func readingOptions(forType type: NSPasteboard.PasteboardType, pasteboard: NSPasteboard) -> NSPasteboard.ReadingOptions {
        return .asData
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM