Reputation: 35070
I'm working on a SwiftUI app for visionOS and I'd like to support multiple windows in my app to enhance the user experience. However, I couldn't find specific documentation or examples on how to achieve this for visionOS.
In SwiftUI for iOS and iPadOS, we can create multiple windows using the .commands
modifier with a CommandGroup
for CommandGroupPlacement.newItem
and then use WindowGroup
to define the second window. However, when trying to apply the same approach in visionOS, I encountered errors like:
Value of type 'some Scene' has no member 'window'
Is there a way to create multiple windows in a SwiftUI app for visionOS? If so, how can I achieve this? Are there any specific APIs or modifiers I should use?
Any insights, code examples, or pointers to relevant documentation would be greatly appreciated.
Upvotes: 8
Views: 6828
Reputation: 17624
You can open a new window using the openWindow environment property.
To start, define your window in the app's Scene and give it an id
:
@main
struct Mail: App {
var body: some Scene {
WindowGroup(id: "mail-viewer") {
MailViewer()
}
}
}
Then, in a view, retrieve the openWindow environment value and call it using the id
from above:
struct NewViewerButton: View {
@Environment(\.openWindow) private var openWindow
var body: some View {
Button("Open new mail viewer") {
openWindow(id: "mail-viewer")
}
}
}
You can also open multiple copies of a window with different input values. First define the view with an input argument:
struct TypeView: View {
var id: Int
init(_ id: Int) {
self.id = id
}
var body: some View {
Text("By Type: \(id)")
}
}
Then, in your main scene, define the WindowGroup:
@main
struct Day007App: App {
var body: some Scene {
WindowGroup() {
ContentView()
}
WindowGroup(id: "type-view", for: Int.self) { $id in
TypeView(id!)
}
}
}
You can now open the window from another view by passing a value in to the openWindow method:
struct ContentView: View {
@Environment(\.openWindow) private var openWindow
var body: some View {
VStack {
Button("Open new window by type") {
openWindow(id: "type-view", value: 1)
}
Button("Open new window by type") {
openWindow(id: "type-view", value: 2)
}
}
}
}
This article contains more information about window management in Swift.
Note that the Window structure is unavailable in VisionOS
Upvotes: 2
Reputation: 81
Apple's Hello World project has great examples on how to open and dismiss different spaces. Here is an implementation of opening two windows programatically. Please note there are some issues with the latest versions of Xcode Beta which cause issues in the Hello World Application.
You need to add an ID for calling your second window:
import SwiftUI
@main
struct ExampleApp: App {
var body: some Scene {
WindowGroup {
PrimaryWindow()
}
WindowGroup (id: "SecondWindow"){
SecondWindow()
}
}
}
Then within the Primary window, declare the openWindow function:
@Environment(\.openWindow) private var openWindow
@Environment(\.dismissWindow) private var dismissWindow
This allows you to open a second window:
openWindow(id: "SecondWindow")
Full code below for PrimaryWindow and SecondWindow
import SwiftUI
import RealityKit
import RealityKitContent
struct PrimaryWindow: View {
@Environment(\.openWindow) private var openWindow
@Environment(\.dismissWindow) private var dismissWindow
var body: some View {
NavigationSplitView {
VStack {
Button(action: {
openWindow(id: "SecondWindow")
print("Show Second Window")
}) {
Text("Show Second Window")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.buttonStyle(.plain)
}
.frame(maxWidth: .infinity, alignment: .top)
.navigationTitle("Sidebar")
} detail: {
Text("Detail")
}
}
}
#Preview {
PrimaryWindow()
}
SecondWindow
import SwiftUI
import RealityKit
import RealityKitContent
struct SecondWindow: View {
@Environment(\.dismissWindow) private var dismissWindow
var body: some View {
NavigationSplitView {
VStack {
Button(action: {
dismissWindow(id: "SecondWindow")
print("Show Second Window")
}) {
Text("Dismiss Second Window")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.buttonStyle(.plain)
}
.frame(maxWidth: .infinity, alignment: .top)
.navigationTitle("Sidebar")
} detail: {
Text("Detail")
}
}
}
#Preview {
SecondWindow()
}
Upvotes: 6