You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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")
            }
        }
    }
}

关键点

  1. 给初始窗口设id:这是标记主窗口的必要操作,不然没法精准指定打开它。
  2. 监听应用激活:用户点应用图标时,应用会激活,这时候检查当前有没有窗口。
  3. 无窗口时主动打开主窗口:如果所有窗口都关了,就调用openWindow(id: "main")创建主窗口。
  4. 延迟检查:防止应用刚激活时窗口状态还没更新,导致误判。

也可以用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:07:08