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

Posted

技术标签:

【中文标题】Xcode Swift MacOS App,将文件拖放到 NSTextField【英文标题】:Xcode Swift MacOS App, drag and drop file into NSTextField 【发布时间】:2021-05-22 15:36:33 【问题描述】:

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

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

谢谢!

【问题讨论】:

文本字段必须是可编辑的,用户才能将内容放入其中 - 你的 B 计划是什么? 你可以继承NSTextField并实现NSDraggingDestination方法 【参考方案1】:

首先,你需要阅读这个guide。

其次,我在这里发布一些代码,用于执行与您所要求的类似的事情。

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

注意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

【讨论】:

非常感谢!一切正常! 弗拉德我可以帮忙。 :) 是否可以在您的解决方案中同时添加双击事件和 cmd+r?如果用户在 NSBox 内双击并清除 TextFIeld 内容,例如用户在应用程序上应用 cmd+r,我想添加打开打开文件对话框的可能性

以上是关于Xcode Swift MacOS App,将文件拖放到 NSTextField的主要内容,如果未能解决你的问题,请参考以下文章

macOS 开发-开发环境与基础工程创建

为`swift package generate-xcodeproj`设置macOS目标?

为`swift package generate-xcodeproj`设置macOS目标?

Swift UI Canvas 未在 macOS 10.15 和 Xcode 11.1 中显示

Xcode Swift 5 MacOs 应用程序存档时为空窗口,但在从 Xcode 运行时有效

强制 Swift Playgrounds (xcode) 在不模拟 iOS 的情况下使用 MacOS SpriteKit