首页 > 解决方案 > 使用 NSEvent 拖动文件时出错。(苹果系统)

问题描述

我想将文件拖到我的窗口,然后执行操作。

我尝试使用此答案中提供的以下片段来区分您是在拖动文件还是窗口。

// In my window controller

class MyWindowController: NSWindowController {
    
    init() {
        // Some initialization steps below are omitted
        let win = NSWindow(...)
        super.init(window: win)
        
        let contentView = DropView(frame: win.frame)
        win.contentView?.addSubview(contentView)

        registerGlobalMouseEvent()
    }

    func registerGlobalMouseEvent() {
        self.window?.acceptsMouseMovedEvents = true
        
        NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDragged, handler: { [self] event in
            // Codes below will cause errors
            let pasteBoard = NSPasteboard(name: .drag)
            guard let fileNames = pasteBoard.propertyList(forType: .init(rawValue: "NSFilenamesPboardType")) as? NSArray else { return }
            let changeCount = pasteBoard.changeCount

            if fileNames.count > 0 && lastChangeCount != changeCount {
                lastChangeCount = changeCount

                // My actions when dragging
            }
        })
    }
    
}

然后我运行我的代码并开始拖动,我得到了三个错误:

[沙盒] 获取沙盒扩展失败

[框架] 无法为 /Users/roy/Downloads/test.txt 发布沙盒扩展,错误 1

[默认] 无法为 URL 颁发沙盒令牌:'file:///Users/roy/Downloads/test.txt' 错误:'Error Domain=NSPOSIXErrorDomain Code=1 "Operation not allowed" UserInfo={NSLocalizedDescription=Cannot issue文件“/Users/roy/Downloads/test.txt”的沙盒扩展:不允许操作}'

 

但是当我这样做的时候

NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDragged, handler: { [self] event in
    // My actions
})

,然后一切顺利。

 

第一个错误似乎无害,因为它并没有阻止我的应用程序运行。

第二个和第三个是致命的,直接导致我的应用程序崩溃。

我想知道他的代码是否有任何问题?任何有用的想法都会很棒!:)


 

标签: swiftmacos

解决方案


使用 sandbox 时,您需要了解Bookmarks 和 Security Scoped URLs 。拖动的 URL 仅授予您的应用程序进程一次读取或读取/写入“用户选择的文件”的权限,具体取决于您配置权利的方式。

您可以保存书签(数据块)以保持对后续会话的访问,只要该文件没有被另一个进程更新,此时书签变得陈旧并且您需要鼓励用户再次选择该文件。

跨 XPC 边界(如共享)将 URL 传递给另一个进程需要您拥有该文件,因此可能涉及到沙箱缓存的副本。

例如:

let dragurl = url_of_dragged_file //at this point you have at-least read access
let cachepath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).last!
let cachedir = URL(fileURLWithPath: cachepath)
let cacheurl = cachedir
  .appendingPathComponent(UUID().uuidString)
  .appendingPathExtension(dragurl.pathExtension)
try FileManager.default.copyItem(at: dragurl, to: cacheurl)

此时,您在本地沙盒缓存中有一个副本,可以将其移交给共享表。


推荐阅读