Awesome
ScenesManager
A SwiftUI package for managing windows and immersive spaces in visionOS applications. ScenesManager provides centralized control over scene lifecycle, visibility tracking, and event handling.
Authors
Created by Tom Krikorian and Tina Debove Nigro
Inspired by VisibilityTrackerModifier from Florent Morin
Features
- Track window and immersive space visibility
- Manage scene lifecycle events
- Suppress window opening and dismissal events
- Simple SwiftUI modifiers for scene tracking
- Immersive space state management
Installation
Add ScenesManager to your project using Swift Package Manager:
dependencies: [
.package(url: "https://github.com/Tab-To-Tap/ScenesManager.git", from: "1.0.0")
]
Quick Start
- Set up the ScenesManager in your main app scene:
@main
struct MyApp: App {
@Environment(\.openWindow) var openWindow
@Environment(\.dismissWindow) var dismissWindow
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
@State private var scenesManager = ScenesManager()
var body: some Scene {
WindowGroup(id: SceneId.mainWindow.rawValue) {
ContentView()
.environment(\.scenesManager, scenesManager)
.onAppear {
scenesManager.setActions(
openWindow: openWindow,
dismissWindow: dismissWindow,
openImmersiveSpace: openImmersiveSpace,
dismissImmersiveSpace: dismissImmersiveSpace
)
}
}
}
}
- Track window lifecycle using the scene tracker modifier:
struct MyView: View {
var body: some View {
ContentView()
.sceneTracker(for: .mainWindow,
onOpen: { print("Window opened") },
onDismiss: { print("Window dismissed") }
)
}
}
- Track immersive spaces:
struct ImmersiveView: View {
var body: some View {
RealityView { content in
// Your RealityKit content
}
.immersiveSpaceTracker()
}
}
Defining Scene Identifiers
Before using ScenesManager, you need to define your scene identifiers. Create an extension of SceneId
in your app:
import ScenesManager
public extension SceneId {
static var mainWindow: Self { .init(rawValue: "MainWindow") }
static var settings: Self { .init(rawValue: "Settings") }
static var immersiveMenu: Self { .init(rawValue: "ImmersiveMenu") }
}
The package already includes the immersiveSpace
identifier that's used internally:
public extension SceneId {
/// The identifier for the immersive space scene
static var immersiveSpace: Self { .init(rawValue: "ImmersiveSpace") }
}
Using Scene Identifiers
Once defined, you can use these identifiers throughout your app:
// In your WindowGroup declarations
WindowGroup(id: SceneId.mainWindow.rawValue) {
ContentView()
}
// When opening windows
scenesManager.openWindow(.settings)
// When tracking scenes
.sceneTracker(for: .mainWindow)
// For immersive spaces
ImmersiveSpace(id: SceneId.immersiveSpace.rawValue) {
ImmersiveView()
}
Usage
Example
A full project is available at ScenesManager-Example
Managing Windows
@Environment(\.scenesManager) var scenesManager
// Open a window
scenesManager.openWindow(.settings)
// Dismiss a window
scenesManager.dismissWindow(.settings)
Event Suppression
// Suppress both open and dismiss events
scenesManager.suppressEvents(.all, for: .settings)
// Suppress only dismiss events
scenesManager.suppressEvents(.dismissEvent, for: .settings)
// Remove suppression
scenesManager.unsuppressEvents(for: .settings)
// Check if events are suppressed
if scenesManager.areEventsSuppressed(.dismissEvent, for: .settings) {
print("Cannot dismiss settings window")
}
Immersive Space Management
// Toggle immersive space
await scenesManager.toggleImmersiveSpace()
// Check immersive space state
switch scenesManager.immersiveSpaceState {
case .open:
print("Immersive space is visible")
case .closed:
print("Immersive space is closed")
case .inTransition:
print("Immersive space is transitioning")
}
Scene State Management
Scenes can be in one of four states:
.opening
: Scene is in the process of opening.opened
: Scene is fully open and active.closing
: Scene is in the process of closing.closed
: Scene is fully closed
// Check scene state
let state = scenesManager.getSceneState(for: .settings)
if state == .opened {
print("Settings scene is fully open")
}
Requirements
- visionOS 2.0+
- Swift 5.9+