Using Push Window to hide the main app window in an immersive space
Sometimes we want to keep an existing window, but show another window while presenting an immersive space.
The Problem
We have a main app window with a complex navigation stack, data, content, controls etc. We don’t want that window showing while the user is in an immersive space. Closing the main window will lose any context our user has in that window.
The Solution
We can use Push Window to replace the main window. This option came with visionOS 2.0 and it can be very useful. If you have ever used the Photos app on visionOS, then you have already seen Push Window in action.
Considering the case above, we want to hide the main window. We can use Push Window to present another window it the same place. The main window is hidden and is restored as soon as the pushed window is closed. I often use this approach to provide controls in the immersive space.
- Media controls
- Maps to navigate a space
- Editing control for immersive content
See also:
Hide a window when presenting an immersive space
Hide a window with an invisible Push Window while presenting an immersive space
Advantages
- We don’t have to modify our main window for this technique to work.
- Easy to set up.
Disadvantages
- This approach only works with one window.
- Moving the pushed window will change the position of the main window, which may not be desirable in some cases.
Let’s see it in action
A video tour of the example. A main window is replaced by the pushed window when the immersive space is opened. The user moves the pushed window, then exits the space. The main window is in the location of the pushed window.
Example Code
In our app view, we defined a couple of window groups.
struct Garden026App: App {
@State private var appModel = AppModel()
var body: some Scene {
WindowGroup(id: "MainWindow") {
ContentView()
.environment(appModel)
}
.defaultSize(CGSize(width: 600, height: 600))
WindowGroup(id: "PushWindow") {
PushWindowContent()
.environment(appModel)
}
.defaultSize(CGSize(width: 300, height: 300))
ImmersiveSpace(id: "GardenScene") {
ImmersiveView()
.environment(appModel)
}
.immersionStyle(selection: .constant(.full), in: .full)
}
}In our ContentView we can add pushWindow
@Environment(\.pushWindow) private var pushWindowWhen we present the immersive space, we can also call pushWindow
Task {
if(appModel.gardenOpen) {
await dismissImmersiveSpace()
return
} else if (!appModel.gardenOpen) {
await openImmersiveSpace(id: "GardenScene")
// Call Push Window
pushWindow(id: "PushWindow")
}
}When we close the immersive space, we can dismiss the pushed window
dismissWindow(id: "PushWindow")Or, if we close the pushed window, we can dismiss the immersive space
if(appModel.gardenOpen) {
await dismissImmersiveSpace()
}You can find the rest of the code in Garden 026 in this repo.
Support our work so we can continue to bring you new examples and articles.

Follow Step Into Vision