How to create multiple windows in a SwiftUI app for visionOS?
Asked Answered
M

2

8

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.

Maryammaryann answered 30/7, 2023 at 13:7 Comment(1)
Can you show which code produced this error? I've added multiple WindowGroup(id: "SomeID")s and opened them with openWindow(id: "SomeID") with the env value @Environment(\.openWindow) private var openWindowNimbostratus
D
6

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

Doxy answered 31/7, 2023 at 20:18 Comment(0)
O
2

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

Ontario answered 26/2 at 0:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.