Xcode Swift MacOS App,将文件拖放到 NSTextField 中

Xcode Swift MacOS App, drag and drop file into NSTextField

提问人:AirMonk 提问时间:2/20/2021 更新时间:2/21/2021 访问量:391

问:

我正在为 MacOS 实现我的第一个应用程序,用户应该输入要处理的文件路径。 我的 NSViewController 应用程序上有一个 NSTextField,我想让用户只需将一个文件拖放到那里,这样我就可以获取文件路径,打开它并在 NSTextField 上放置一些带有文件信息的文本。

你能帮帮我吗?我看到如果我使 NSTextField 可编辑,我可以删除文件,但我不希望 NSTextField 是可编辑的(只能选择复制粘贴信息)

谢谢!

Swift Xcode macOS Cocoa 拖放

评论

1赞 red_menace 2/20/2021
textField 需要是可编辑的,以便用户将某些内容放入其中 - 您的计划 B 是什么?
1赞 Alexander 2/20/2021
可以子类化和实现 NSDraggingDestination 方法NSTextField

答:

2赞 jvarela 2/21/2021 #1

首先,您需要阅读本指南

其次,我在这里发布一些代码,我用它们来做一些类似于你所要求的事情。

但是,我的策略不是子类化,而是将此字段放在子类中。这样做的优点是可以使用对焦环向用户提供一些视觉反馈。NSTextFieldNSBox

注意通过窗口控制器设置字符串值的位置,然后将其转发到文本字段,以将其字符串值设置为已删除文件的路径。performDragOperation

您可以按 筛选可以接受的内容。也要检查一下。prepareForDragOperation

class DropBox: NSBox
{
     let dragType = NSPasteboard.PasteboardType(kUTTypeFileURL as String)
     var doHighlight = false

// ---------------------------------------------------------------------------------
// awakeFromNib
// ---------------------------------------------------------------------------------
override func awakeFromNib()
{
    registerForDraggedTypes([dragType])
}

// ---------------------------------------------------------------------------------
// acceptsFirstMouse
// ---------------------------------------------------------------------------------
// Accept activation click as click in window, so source doesn't have to be the
// active window
override func acceptsFirstMouse(for event: NSEvent?) -> Bool
{
    return true
}

// ---------------------------------------------------------------------------------
// draggingEntered
// ---------------------------------------------------------------------------------
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation
{
    let pasteboard = sender.draggingPasteboard
    let mask = sender.draggingSourceOperationMask
    
    if let types = pasteboard.types, types.contains(dragType)
    {
        if mask.contains(.link)
        {
            doHighlight = true
            needsDisplay = true
            return .link
        }
    }
    
    return []
}

// ---------------------------------------------------------------------------------
// draggingExited
// ---------------------------------------------------------------------------------
override func draggingExited(_ sender: NSDraggingInfo?)
{
    doHighlight = false
    needsDisplay = true
}

// ---------------------------------------------------------------------------------
// drawRect
// ---------------------------------------------------------------------------------
override func draw(_ dirtyRect: NSRect)
{
    super.draw(dirtyRect)
    
    if doHighlight {
        let rect = NSRect(x: dirtyRect.origin.x,
                          y: dirtyRect.origin.y,
                          width: NSWidth(dirtyRect),
                          height: NSHeight(dirtyRect) - NSHeight(titleRect) + 1.0)
        
        NSFocusRingPlacement.only.set()
        let contentRect = rect.insetBy(dx: 4, dy: 4)
        NSBezierPath(rect: contentRect).fill()
    }
}

// ---------------------------------------------------------------------------------
// performDragOperation
// ---------------------------------------------------------------------------------
// Method to handle drop data
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool
{
    if let source = sender.draggingSource as? NSBox {
        if source === self {
            return false
        }
    }
    
    let pasteboard = sender.draggingPasteboard
    let options = [NSPasteboard.ReadingOptionKey.urlReadingFileURLsOnly:true]
    if let urls = pasteboard.readObjects(forClasses: [NSURL.self], options: options) as? [URL],
        let controller = self.window?.delegate as? WindowController
    {
        for url in urls {
            if SchISCoreFileUtilities.isValid(url.path) {
                controller.setApplicationPath(url.path)
                return true
            }
        }
    }
    
    return false
}

// ---------------------------------------------------------------------------------
// prepareForDragOperation
// ---------------------------------------------------------------------------------
// Method to determine if we can accept the drop (filter for urls to apps)
override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool
{
    doHighlight = false
    needsDisplay = true
    let pasteboard = sender.draggingPasteboard
    
    if let types = pasteboard.types, types.contains(dragType)
    {
        let options = [NSPasteboard.ReadingOptionKey.urlReadingFileURLsOnly:true]
        if let urls = pasteboard.readObjects(forClasses: [NSURL.self], options: options) as? [URL]
        {
            for url in urls {
                if url.pathExtension == "app" {
                    return true
                }
            }
        }
    }
    
    return false
}

}

评论

0赞 AirMonk 3/7/2021
是否可以在您的解决方案中添加双击事件和 cmd+r?我想添加打开文件对话框的可能性,如果用户在 NSBox 内双击并清除 TextFIeld 内容,例如,如果用户在应用程序上应用 cmd+r