Unable to Create a Fully Immersive Experience That Hides Other Windows in visionOS App

Description: I'm developing a travel/panorama viewing app for visionOS that allows users to view 360° panoramic images in an immersive space. When users enter panorama viewing mode, I want to provide a fully immersive experience where the main interface window and Earth 3D globe window are hidden. I've implemented the app following Apple's documentation on Creating Fully Immersive Experiences, but when users enter the immersive space, both the main window and the Earth 3D window remain visible, diminishing the immersive experience.

Implementation Details: My app has three main components: A main content window showing panorama thumbnails A 3D globe window (volumetric) showing locations An immersive space for viewing 360° panoramas I'm using .immersionStyle(selection: $panoImageView, in: .full) to create a fully immersive experience, but other windows remain visible.

Relevant Code: @main struct Travel_ImmersiveApp: App { @StateObject private var appModel = AppModel() @State private var panoImageView: ImmersionStyle = .full

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appModel)
}
.windowStyle(.automatic)
.defaultSize(width: 1280, height: 825)
WindowGroup(id: "Earth") {
Globe3DView()
.environmentObject(appModel)
.onAppear {
appModel.isGlobeWindowOpen = true
appModel.globeWindowOpen = true
}
.onDisappear {
if !appModel.shouldCloseApp {
appModel.handleGlobeWindowClose()
}
}
}
.windowStyle(.volumetric)
.defaultSize(width: 0.8, height: 0.8, depth: 0.8, in: .meters)
.windowResizability(.contentSize)
ImmersiveSpace(id: "ImmersiveView") {
ImmersiveView()
.environmentObject(appModel)
}
.immersionStyle(selection: $panoImageView, in: .full)
}

}

Opening the Immersive Space: func getPanoImageAndOpenImmersiveSpace() async { appModel.clearMemoryCache()

do {
let canView = appModel.canViewImage(image)
if canView {
let downloadedImage = try await appModel.getPanoramaImage(for: image) { progress in
Task { @MainActor in
cardState = .loading(progress: progress)
}
}
await MainActor.run {
appModel.updateCurrentImage(image, panoramaImage: downloadedImage)
}
if !appModel.immersiveSpaceOpened {
try await openImmersiveSpace(id: "ImmersiveView")
await MainActor.run {
appModel.immersiveSpaceOpened = true
cardState = .normal
}
} else {
await MainActor.run {
appModel.updateImmersiveView = true
cardState = .normal
}
}
} else {
await MainActor.run {
appModel.errorMessage = "You do not have permission to view this image."
cardState = .normal
}
}
} catch {
// Error handling
}

}

Immersive View Implementation: struct ImmersiveView: View { @EnvironmentObject var appModel: AppModel

var body: some View {
RealityView { content in
let rootEntity = Entity()
content.add(rootEntity)
Task {
if let selectedImage = appModel.selectedImage,
appModel.canViewImage(selectedImage) {
await loadPanorama(for: rootEntity)
}
}
} update: { content in
if appModel.updateImmersiveView,
let selectedImage = appModel.selectedImage,
appModel.canViewImage(selectedImage),
let rootEntity = content.entities.first {
Task {
await loadPanorama(for: rootEntity)
appModel.updateImmersiveView = false
}
}
}
.onAppear {
print("ImmersiveView appeared")
}
.onDisappear {
appModel.resetImmersiveState()
}
}
// loadPanorama implementation...

}

What I've Tried Set immersionStyle to .full as recommended in the documentation Confirmed that the immersive space is properly opened and displaying panoramas Verified that the state management for the immersive space is working correctly Questions How can I ensure that when the user enters the immersive panorama viewing experience, all other windows (main interface and Earth 3D globe) are automatically hidden? Is there a specific API or approach I'm missing to properly implement a fully immersive experience that hides all other windows? Do I need to manually dismiss the windows when opening the immersive space, and if so, what's the best approach for doing this? Any guidance or sample code would be greatly appreciated. Thank you!

Answered by DTS Engineer in 833721022

Hello @Travel_Immersive,

As noted in ImmersiveSpace documentation, "When your app opens an immersive space, the system hides all other visible apps."

The system does not hide your app's windows automatically. If you would like to dismiss your app's windows when you open your immersive space, use the dismissWindow action.

-- Greg

Hello @Travel_Immersive,

As noted in ImmersiveSpace documentation, "When your app opens an immersive space, the system hides all other visible apps."

The system does not hide your app's windows automatically. If you would like to dismiss your app's windows when you open your immersive space, use the dismissWindow action.

-- Greg

Hello Greg, @DTS Engineer Thank you for your previous guidance on using the dismissWindow action. Following your advice, I've successfully implemented window dismissal when entering the immersive panorama viewing experience, which has greatly improved the immersive feeling for users. Here's my current implementation: // When entering immersive panorama mode, I dismiss other windows func getPanoImageAndOpenImmersiveSpace() async { // ...existing code...

if !appModel.immersiveSpaceOpened {
// Dismiss main window and Earth window before opening immersive space
dismissWindow(id: "MainWindow")
dismissWindow(id: "Earth")
try await openImmersiveSpace(id: "ImmersiveView")
await MainActor.run {
appModel.immersiveSpaceOpened = true
cardState = .normal
}
}
// ...remaining code...

This works well for entering the immersive mode - all other windows are properly hidden. However, I'm facing a new challenge: when exiting the immersive panorama view, I can't properly restore the previous window state. My current exit implementation: func exitPanoramaView() { Task { // Close immersive space await dismissImmersiveSpace()

// Reset immersive state
appModel.resetImmersiveState()
// Close panorama menu
appModel.updateWindowExistence(id: "PanoramaMenu", exists: false)
dismissWindow(id: "PanoramaMenu")
// Try to reopen main window and Earth window
openWindow(id: "MainWindow")
if appModel.isGlobeWindowOpen {
openWindow(id: "Earth")
}
}

}

While this reopens the windows, they appear in their initial state rather than preserving the state they had before entering the immersive mode (such as scroll position in the main window, or the specific view of the globe in the Earth window).

Is there a recommended approach to: Preserve window state when dismissing windows before entering immersive mode? Restore windows with their previous state when exiting immersive mode? Handle the transition between regular windows and immersive space more smoothly? Any further guidance or code examples would be greatly appreciated. Thank you for your assistance.

Hello @Travel_Immersive,

See Restoring Your App’s State with SwiftUI.

The sample code does not support visionOS, but the APIs are available on visionOS and the principles would be the same.

--Greg

Unable to Create a Fully Immersive Experience That Hides Other Windows in visionOS App
 
 
Q