macOS 场景参考
适用于 macOS 应用的 SwiftUI 场景类型——
Settings、MenuBarExtra、WindowGroup、Window、UtilityWindow和DocumentGroup。涵盖 macOS 专属场景以及带 macOS 特定行为的跨平台场景。
目录
- 快速查找表
- Settings(macOS 专属)
- MenuBarExtra(macOS 专属)
- WindowGroup(macOS 行为)
- Window
- UtilityWindow(macOS 专属)
- DocumentGroup
- 平台条件编译
- 最佳实践
快速查找表
| API | 可用性 | 仅 macOS? | macOS 特定行为 |
|---|---|---|---|
WindowGroup | macOS 11.0+ | 否 | 多窗口实例、标签式界面、自动 Window 菜单命令 |
Window | macOS 13.0+ | 否 | 唯一窗口关闭时应用退出;将自己添加到 Windows 菜单 |
UtilityWindow | macOS 15.0+ | 是 | 浮动工具面板;从活动主窗口接收FocusedValues |
Settings | macOS 11.0+ | 是 | 呈现偏好设置窗口(Cmd+,) |
MenuBarExtra | macOS 13.0+ | 是 | 系统菜单栏中的持久图标/菜单 |
DocumentGroup | macOS 11.0+ | 否 | 基于文档的菜单栏命令(File > New/Open/Save);多文档窗口 |
Settings(macOS 专属)
呈现应用的偏好设置窗口,可通过Cmd+,或应用菜单访问。SwiftUI 自动启用 Settings 菜单项并管理窗口生命周期。
Settings{TabView{Tab("General",systemImage:"gear"){GeneralSettingsView()}Tab("Advanced",systemImage:"star"){AdvancedSettingsView()}}.scenePadding().frame(maxWidth:350,minHeight:100)}多面板偏好设置使用带Tab项的TabView。每个标签页的内容通常是带@AppStorage支持控件的Form。
SettingsLink(macOS 14.0+)
打开 Settings 场景的按钮。用于应用内导航到偏好设置。
structSidebarFooter:View{varbody:someView{SettingsLink{Label("Preferences",systemImage:"gear")}}}openSettings 环境动作(macOS 14.0+)
以编程方式打开(或前置)Settings 窗口。
structOpenSettingsButton:View{@Environment(\.openSettings)privatevaropenSettingsvarbody:someView{Button("Open Settings"){openSettings()}}}MenuBarExtra(macOS 专属)
在系统菜单栏中渲染一个持久控件。有两种样式:
.menu(默认)——标准下拉菜单.window——带自定义 SwiftUI 视图的弹出面板
菜单样式(下拉)
MenuBarExtra("My Utility",systemImage:"hammer"){Button("Action One"){/* ... */}Button("Action Two"){/* ... */}Divider()Button("Quit"){NSApplication.shared.terminate(nil)}}窗口样式(弹出面板)
MenuBarExtra("Status",systemImage:"chart.bar"){DashboardView().frame(width:240)}.menuBarExtraStyle(.window)变体:
- 可切换——传递
isInserted:并带@AppStorage绑定,让用户显示/隐藏该附加项:MenuBarExtra("Status", systemImage: "chart.bar", isInserted: $showMenuBarExtra) - 纯菜单栏应用——将
MenuBarExtra作为唯一场景 + 在 Info.plist 中设置LSUIElement = true隐藏 Dock 图标。如果用户从菜单栏移除该附加项,应用会自动终止。
WindowGroup(macOS 行为)
在 macOS 上,WindowGroup支持:
- 多窗口实例——用户可以从 File > New Window 打开多个窗口
- 标签式界面——用户可以将窗口合并为标签页
- 自动 Window 菜单——窗口管理命令自动出现
@mainstructMail:App{varbody:someScene{// 基本多窗口支持WindowGroup{MailViewer()}// 以编程方式打开的数据展示窗口WindowGroup("Message",for:Message.ID.self){$messageIDinMessageDetail(messageID:messageID)}}}// 以编程方式打开特定窗口structNewMessageButton:View{varmessage:Message@Environment(\.openWindow)privatevaropenWindowvarbody:someView{Button("Open Message"){openWindow(value:message.id)}}}与
Window的关键区别:WindowGroup即使所有窗口都关闭也会保持应用运行。Window(作为唯一场景)在关闭时会退出应用。
Window
单个、唯一的窗口场景。系统确保只存在一个实例。
@mainstructMail:App{varbody:someScene{WindowGroup{MailViewer()}// 补充性的单例窗口Window("Connection Doctor",id:"connection-doctor"){ConnectionDoctor()}}}// 以编程方式打开——如果已打开则前置structOpenDoctorButton:View{@Environment(\.openWindow)privatevaropenWindowvarbody:someView{Button("Connection Doctor"){openWindow(id:"connection-doctor")}}}作为唯一场景的 Window
如果Window是唯一场景,窗口关闭时应用退出:
@mainstructVideoCall:App{varbody:someScene{Window("VideoCall",id:"main"){CameraView()}}}建议:在大多数情况下,主场景优先使用
WindowGroup。对补充性的单例窗口使用Window。
UtilityWindow(macOS 专属)
用于工具面板和检查器面板的专用浮动窗口。自 macOS 15.0 起可用。
关键行为:
- 从聚焦的主场景接收
FocusedValues(像菜单栏命令一样) - 浮动在主窗口之上(默认层级:
.floating) - 应用不再活跃时隐藏
- 仅在明确需要时才获得焦点(例如点击标题栏)
- 可用 Escape 键关闭
- 默认不可最小化
- 自动向 View 菜单添加显示/隐藏项
@mainstructPhotoBrowser:App{varbody:someScene{WindowGroup{PhotoGallery()}UtilityWindow("Photo Info",id:"photo-info"){PhotoInfoViewer()}}}structPhotoInfoViewer:View{// 根据哪个主窗口被聚焦自动更新@FocusedValue(PhotoSelection.self)privatevarselectedPhotosvarbody:someView{ifletphotos=selectedPhotos{Text("\(photos.count)photos selected")}else{Text("No selection").foregroundStyle(.secondary)}}}提示:使用
.commandsRemoved()移除自动的 View 菜单项,并在命令中的其他位置放置WindowVisibilityToggle。
DocumentGroup
带自动文件管理的文档型应用。在 macOS 上提供:
- 基于文档的菜单栏命令(File > New、Open、Save、Revert)
- 同时多个文档窗口
- 在 iOS 上,显示文档浏览器
DocumentGroup(newDocument:TextFile()){configinContentView(document:config.$document)}文档类型必须遵循FileDocument(值类型)或ReferenceFileDocument(引用类型)。关键要求:
structTextFile:FileDocument{staticvarreadableContentTypes:[UTType]{[.plainText]}vartext:String=""init(){}init(configuration:ReadConfiguration)throws{text=String(data:configuration.file.regularFileContents??Data(),encoding:.utf8)??""}funcfileWrapper(configuration:WriteConfiguration)throws->FileWrapper{FileWrapper(regularFileWithContents:Data(text.utf8))}}对于多种文档类型,添加额外的DocumentGroup场景——对只读格式使用DocumentGroup(viewing:)。
平台条件编译
始终将 macOS 专属场景包裹在#if os(macOS)中:
@mainstructMyApp:App{varbody:someScene{WindowGroup{ContentView()}#ifos(macOS)Settings{SettingsView()}MenuBarExtra("Status",systemImage:"bolt"){StatusMenu()}#endif}}最佳实践
- 偏好设置使用
Settings——优先于自定义偏好设置窗口 - 菜单栏项使用
MenuBarExtra——优先于直接管理 AppKit 的NSStatusItem - 主场景使用
WindowGroup——将Window保留给补充性单例 - 检查器/面板使用
UtilityWindow——它自动处理浮动、焦点和可见性 - 文档型应用使用
DocumentGroup——它提供完整的 File 菜单和文档生命周期 - 用
#if os(macOS)门控 macOS 专属场景,用于多平台项目 - 使用
openWindow(id:)以编程方式打开窗口——它会把已有窗口前置