如何在visionOS的SwiftUI应用中实现多窗口功能?
如何在visionOS的SwiftUI应用中实现多窗口功能?
嘿,我刚好研究过visionOS里的多窗口实现,确实iOS那套直接搬过来会踩坑——因为visionOS的窗口模型是基于3D空间设计的,和iOS/iPadOS的平面窗口体系有差异,你遇到的Value of type 'some Scene' has no member 'window'错误就是因为这个:visionOS的Scene类型压根没有window这个属性,原来的iOS写法自然行不通。
不过别担心,用SwiftUI实现visionOS多窗口其实很直观,核心就是用WindowGroup配合openWindow环境动作,下面是具体的实现步骤和代码示例:
1. 定义多个窗口组
首先在你的App结构体里,为每个需要的窗口类型声明一个WindowGroup,并给它们设置唯一的id来区分:
@main struct VisionMultiWindowApp: App { var body: some Scene { // 主窗口(默认打开的窗口) WindowGroup { MainView() } // 第二个独立窗口,必须设置唯一id WindowGroup(id: "SecondaryWindow") { SecondaryView() } // 可以继续添加更多窗口组 WindowGroup(id: "DataWindow") { DataView() } } }
2. 在视图中触发打开新窗口
在需要打开新窗口的视图里,注入openWindow环境动作,通过指定窗口组的id来唤起对应的窗口:
struct MainView: View { // 注入打开窗口的环境动作 @Environment(\.openWindow) private var openWindow var body: some View { VStack(spacing: 20) { Text("主窗口") .font(.largeTitle) Button("打开第二个窗口") { // 通过id指定要打开的窗口组 openWindow(id: "SecondaryWindow") } .buttonStyle(.borderedProminent) Button("打开带数据的窗口") { // 还可以给新窗口传递初始数据 openWindow(id: "DataWindow", defaultValue: "来自主窗口的问候!") } .buttonStyle(.bordered) } .padding() } } // 第二个窗口的视图 struct SecondaryView: View { var body: some View { VStack { Text("第二个独立窗口") .font(.largeTitle) Text("在visionOS里,我可以被自由拖动、缩放哦~") .foregroundColor(.secondary) } .padding() } }
3. 接收传递给新窗口的数据
如果需要给新窗口传递数据,可以在目标视图里用@Environment(\.openWindowParameter)接收:
struct DataView: View { // 接收从主窗口传递过来的数据 @Environment(\.openWindowParameter) private var receivedMessage: String? var body: some View { VStack(spacing: 20) { Text("数据窗口") .font(.largeTitle) if let message = receivedMessage { Text("收到的消息:\(message)") .font(.title) .foregroundColor(.blue) } } .padding() } }
4. 添加菜单命令(可选)
如果你想让用户通过菜单栏打开新窗口,可以在App结构体里添加CommandMenu:
@main struct VisionMultiWindowApp: App { @Environment(\.openWindow) private var openWindow var body: some Scene { // ... 之前的窗口组定义 ... // 添加自定义菜单 CommandMenu("窗口") { Button("新建第二个窗口") { openWindow(id: "SecondaryWindow") } Button("新建数据窗口") { openWindow(id: "DataWindow", defaultValue: "从菜单打开的窗口!") } } } }
一些关键注意事项
- 每个
WindowGroup必须设置唯一的id,否则系统无法区分不同的窗口类型 - visionOS的窗口是悬浮在3D空间中的,用户可以自由拖动、缩放、调整位置,不需要开发者手动处理这些交互
- 如果你需要控制窗口的初始大小或位置,可以给
WindowGroup添加.windowResizability或.defaultSize等修饰符,比如:
WindowGroup(id: "SecondaryWindow") { SecondaryView() } .defaultSize(width: 400, height: 300) .windowResizability(.contentSize)
备注:内容来源于stack exchange,提问作者János




