You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动