NSSavePanel 是 AppKit 提供的类,在 macOS 中用于保存文件时弹出的系统对话框,允许用户选择保存路径、文件名、文件类型等。
基本用法
let savePanel = NSSavePanel()
savePanel.allowedFileTypes = ["txt"]
savePanel.nameFieldStringValue = "MyFile"
savePanel.canCreateDirectories = true
savePanel.begin { response in
if response == .OK {
if let url = savePanel.url {
// 在这里保存文件内容到 url 路径
try? "Hello, world!".write(to: url, atomically: true, encoding: .utf8)
}
}
}

常用属性
1、allowedFileTypes:允许保存的文件类型扩展名,如 [“jpg”, “png”]。
panel.allowedFileTypes = ["txt", "md"]
只允许保存为 .txt 或 .md 类型文件。
如果在保存文件的时候,填写的文件名不在文件类型扩展列表中,默认以首个文件类型保存。
当文件类型扩展名设置为[“txt”,”md”],保存文件为MyFile.png时,实际保存的文件名称为MyFile.png.txt。

2、nameFieldStringValue:默认的文件名。
panel.nameFieldStringValue = "MyDocument.txt"
面板打开时默认文件名为 “MyDocument.txt”。
3、directoryURL:默认保存位置(URL),是一个 URL 类型的目录地址。
panel.directoryURL = FileManager.default.homeDirectoryForCurrentUser
可以设置directoryURL,选择打开的目录地址。
4、canCreateDirectories:是否允许新建文件夹。
panel.canCreateDirectories = true
用户可点击“新建文件夹”按钮。

5、isExtensionHidden:是否隐藏扩展名(默认 false)。
panel.isExtensionHidden = true
文件名输入框中不显示扩展名(如 .txt)。
6、message:顶部提示信息。
panel.message = "请选择保存位置和文件名"
会显示在面板顶部、按钮上方。

7、prompt:按钮文字(默认是“保存”)。
panel.prompt = "导出"
按钮会显示为“导出”而不是“保存”。

8、title:面板窗口的标题。
panel.title = "保存图片"

9、showsTagField:是否显示 macOS 的“标签”字段。
panel.showsTagField = true
macOS Ventura (13) 及以后系统中,showsTagField = false 在系统层面上被忽略,即使设置为 false,标签字段仍然会显示。这是 macOS 的行为变化,Apple 没有提供禁用标签字段的公开方式。
目前无正式方式隐藏标签字段,Apple 没有提供任何 API 来彻底关闭这个 UI 元素。如果想保持 UI 简洁,只能接受它显示,或者等待未来 macOS 系统可能做出改变。
10、treatsFilePackagesAsDirectories:是否将文件包(如 .app、.pages 文件)当作普通文件夹显示。
panel.treatsFilePackagesAsDirectories = false

11、isExpanded:初始时是否展开面板(显示侧边栏等)。
print(“panel.isExpanded”)
isExpanded是只读属性,可以根据isExpanded判断是否展开面板。

虽然 isExpanded 是只读属性,但可以通过 KVC 强制展开 NSSavePanel:
let panel = NSSavePanel()
// 使用 KVC 强制设置为展开状态
panel.setValue(true, forKey: "isExpanded")
// 其他常规设置
panel.allowedFileTypes = ["txt"]
panel.nameFieldStringValue = "example.txt"
if panel.runModal() == .OK {
if let url = panel.url {
print("保存到:\(url.path)")
}
}
注意:这属于使用“私有 API”,虽然没风险但苹果没有官方保证此方式长期有效。
这个方法在 macOS 12、13、14(Ventura、Sonoma)中测试有效。
12、allowsOtherFileTypes:是否允许保存为未列在 allowedFileTypes 的扩展名。
panel.allowsOtherFileTypes = false
13、showsHiddenFiles:是否显示隐藏文件(macOS 中以点开头的文件)。
panel.showsHiddenFiles = true
异步与同步调用
异步方式(推荐,适用于 SwiftUI / Cocoa App):
savePanel.begin { response in ... }
同步方式(阻塞线程,适合简单用法):
if savePanel.runModal() == .OK {
if let url = savePanel.url {
// 保存文件
}
}
使用场景
1、导出文件。
用户点击“导出”或“保存”按钮,弹出保存对话框。
func exportButtonClicked() {
let savePanel = NSSavePanel()
savePanel.allowedFileTypes = ["txt"]
savePanel.nameFieldStringValue = "导出文件.txt"
savePanel.begin { response in
if response == .OK, let url = savePanel.url {
try? "导出的内容".write(to: url, atomically: true, encoding: .utf8)
}
}
}
2、自定义格式文件(.myappdata)。
3、图片/日志/配置保存。
例如做了一个绘图应用,用户可以保存画布为 PNG。
let image = canvasView.snapshot() // 假设你生成了一个 NSImage
let savePanel = NSSavePanel()
savePanel.allowedFileTypes = ["png"]
savePanel.nameFieldStringValue = "MyDrawing.png"
savePanel.begin { response in
if response == .OK, let url = savePanel.url {
let tiffData = image.tiffRepresentation
let bitmap = NSBitmapImageRep(data: tiffData!)
let pngData = bitmap?.representation(using: .png, properties: [:])
try? pngData?.write(to: url)
}
}
4、快速提示用户自选位置保存临时文件。
总结
1、NSSavePanel:展示“保存文件”对话框。
2、NSOpenPanel:展示“打开文件”对话框。
3、推荐方法:begin { response in … }(非阻塞)。
相关文章
1、Swift 管理文件的FileManager类:https://fangjunyu.com/2024/11/03/swift-%e7%ae%a1%e7%90%86%e6%96%87%e4%bb%b6%e7%9a%84filemanager%e7%b1%bb/
2、Swift URL.documentsDirectory和FileManager 类的关系:https://fangjunyu.com/2024/11/03/swift-url-documentsdirectory%e5%92%8cfilemanager-%e7%b1%bb%e7%9a%84%e5%85%b3%e7%b3%bb/