visionOS中如何让初始WindowGroup在启动时重新打开?
问题
我在做visionOS应用时遇到了窗口恢复的问题:
- 应用里有一个无参数的初始
WindowGroup,启动时能正常打开;同时还有几个带参数的WindowGroup(for:)窗口。 - 打开带参数的窗口后关掉初始窗口,没法再恢复初始窗口;就算把带参数的窗口也关掉,点应用图标只会重新打开那个带参数的窗口,初始窗口还是不出来。
- 现在只能强制退出重启才能找回初始窗口,我想实现像Safari那样的效果:把所有窗口都关了之后,点应用图标能自动重新打开主窗口。
- 试过给初始
WindowGroup加id,虽然能从其他窗口用openWindow打开,但应用启动时不会自动显示这个初始窗口。
可复现代码
import SwiftUI @main struct Limbo_MREApp: App { var body: some Scene { // I've also tried this form, same result // WindowGroup(id: "main") { WindowGroup { ContentView() } WindowGroup(for: String.self) { $string in if let string { Text(string) .font(.extraLargeTitle2) } } } } struct ContentView: View { @Environment(\.openWindow) var openWindow var body: some View { VStack { Text("Hello, world!") Button("Tap Me", systemImage: "doc.on.doc") { openWindow(value: "button tapped") } } } }
Info.plist配置说明
已开启Supports Multiple Windows(多窗口支持)。
解决办法
要实现这个效果,得给初始窗口组设个明确的id,再监听应用激活状态,发现没窗口的时候主动打开主窗口。
改好的代码如下:
import SwiftUI import AppKit @main struct Limbo_MREApp: App { @Environment(\.openWindow) private var openWindow @State private var didCheck = false var body: some Scene { // 给主窗口组加个唯一id WindowGroup(id: "main") { ContentView() } WindowGroup(for: String.self) { $string in if let string { Text(string) .font(.extraLargeTitle2) } } .onChange(of: NSApplication.shared.isActive) { isActive in // 应用激活时检查窗口状态,避免重复触发 guard isActive, !didCheck else { return } didCheck = true // 等一小会儿确保窗口状态更新完 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // 检查有没有可见窗口 let hasVisibleWindows = NSApplication.shared.windows.contains { !$0.isHidden } if !hasVisibleWindows { // 打开主窗口 openWindow(id: "main") } didCheck = false } } } } struct ContentView: View { @Environment(\.openWindow) var openWindow var body: some View { VStack { Text("Hello, world!") Button("Tap Me", systemImage: "doc.on.doc") { openWindow(value: "button tapped") } } } }
关键点
- 给初始窗口设id:这是标记主窗口的必要操作,不然没法精准指定打开它。
- 监听应用激活:用户点应用图标时,应用会激活,这时候检查当前有没有窗口。
- 无窗口时主动打开主窗口:如果所有窗口都关了,就调用
openWindow(id: "main")创建主窗口。 - 延迟检查:防止应用刚激活时窗口状态还没更新,导致误判。
也可以用ScenePhase来监听应用状态,替换原来的onChange部分即可:
@Environment(\.scenePhase) private var scenePhase // 替换原来的onChange代码块 .onChange(of: scenePhase) { newPhase in guard newPhase == .active else { return } didCheck = true DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { let hasVisibleWindows = NSApplication.shared.windows.contains { !$0.isHidden } if !hasVisibleWindows { openWindow(id: "main") } didCheck = false } }
为啥之前设id没用?
只给初始窗口加id的话,visionOS默认只会恢复之前关闭过的窗口。你把初始窗口关了之后,系统不会主动重新创建它,得我们自己在应用激活时主动触发打开操作。
内容的提问来源于stack exchange,提问作者Hal Mueller
相关产品推荐
相关产品推荐

