How to transition from one immersive space to another – Part Two
Using a loading window as a launch pad from one immersive space to another.
In Part One we raised this caveat:
The problem: in visionOS at least one app scene (WindowGroup or ImmersiveSpace) must be open, but when an ImmersiveSpace is open, we can’t open another one.
We were able to work around the issue using the main window. But what if our app needs to hide the main window when a user is immersed in a space? We can’t go directly from one space to another, so we need to open a window.
Watch the video first, then we’ll cover the details.
A video demo of moving from one scene to another. The user taps a sphere to indicate which space they want to visit. The app opens a loading window, closes the current scene, and opens the next one.
What is happening here?
- The app opens with a main window, which includes a button to enter the blue dome.
- Inside the blue dome, we can tap on the red or green spheres to change scenes. Doing so will open a small loading window.
- When this loading window appears, it fires a task to do a few things
- Dismiss any open space
- Wait one second (optional, but it helps to show the full transition as the loading indicator spins)
- Get some data from the app model and use it to open a new space.
- Close the loading window
- Each dome contains the same spheres to navigate to the other spaces. Tapping on the sphere that matches the color of the current dome with exit the space.
// LoadingWindow
.onAppear() {
Task {
if(appModel.immersiveSpaceActive) {
await dismissImmersiveSpace()
try? await Task.sleep(nanoseconds: 100_000_000_0)
if let space = appModel.spaceToOpen {
await openImmersiveSpace(id: space)
appModel.spaceToOpen = nil
dismissWindow(id: "LoadingWindow")
}
}
}
}Once again, we can thank Scene Phase for the magic here. Each scene tracks its own scene phase and updates the app model. We can then use the app model as a central source of truth for the state of our scenes.
About the loading window
We can improve our loading window to make it feel a bit more immersive than just a floating rectangle.
Hide the glass background for the window
WindowGroup(id: "LoadingWindow") {
LoadingWindow()
.environment(appModel)
}
.windowStyle(.plain) // hide the glass background for the window
.defaultSize(CGSize(width: 400, height: 200))Hide the window bar
.persistentSystemOverlays(.hidden)Load a Reality Composer Pro scene instead of 2D content. This scene has a small collection of spheres which will rotate around an axis using a repeating timeline.
RealityView { content in
if let root = try? await Entity(named: "Loading", in: realityKitContentBundle) {
root.scale = [0.1, 0.1, 0.1]
content.add(root)
}
}
Sample code is available in Garden19 in the Step Into Example Projects repo.
Support our work so we can continue to bring you new examples and articles.

Follow Step Into Vision