TechnologiesSwiftUIImmersive Spaces and visionOS

ImmersionChangeContext struct

iOSmacOStvOSwatchOSvisionOS✓ renders

A structure that represents a state of immersion of your app.

How it works

ImmersionChangeContext describes the state of immersion at a single moment in a visionOS scene, exposing how much of the surrounding passthrough the system has replaced with your immersive content. SwiftUI hands you these values in pairs — the immersion as it was and as it now is — so you can observe transitions rather than poll a property. Reach for it inside an immersion-change handler when you need to react to the user dialing immersion up or down, for example to fade audio, swap a skybox, or adjust scene lighting as the world opens up around them.

  1. Receive old and new contexts in onImmersionChange

    The handler attached with onImmersionChange fires whenever the immersion level moves, and SwiftUI passes two ImmersionChangeContext values into the closure: the state before the change and the state after it. In the example these arrive as oldContext and newContext, giving you both ends of the transition in one call so you can compare them or compute a delta.

  2. Read the immersion through the amount property

    ImmersionChangeContext surfaces the current depth of immersion as a normalized value you read from its amount member. The closure reads oldContext.amount and newContext.amount to see where immersion started and where it ended up — each is a fraction of the way between no immersion and full immersion, the same scale the surrounding Slider drives from 0...1.

  3. Drive side effects from the transition

    Because you hold both contexts at once, you can branch on the direction or magnitude of the change rather than just the latest value. Here the handler simply logs "from \(oldContext.amount) to \(newContext.amount)", but the same pair is where you would trigger work that should happen as immersion rises or falls.

  4. Attach the observer where the scene lives

    ImmersionChangeContext is delivered through a view modifier, so onImmersionChange plugs onto the view whose immersion you care about — in the example it sits on the padded VStack. The symbol itself carries no initializer you call; you consume the contexts SwiftUI constructs for you when the level changes.

Try it — Replace the print body with if newContext.amount > oldContext.amount { print("deeper") } to watch ImmersionChangeContext distinguish increasing immersion from decreasing as you drag the Slider.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

ImmersionChangeContext.swift
struct ImmersionChangeContextDemo: View {
    @State private var amount = 0.0

    var body: some View {
        VStack(spacing: 12) {
            Text("Immersion: \(Int(amount * 100))%")
                .font(.headline)
            Slider(value: $amount, in: 0...1)
        }
        .padding()
        .onImmersionChange { oldContext, newContext in
            print("from \(oldContext.amount) to \(newContext.amount)")
        }
    }
}
Live preview
Immersion: 0%
Immersion: 0%
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →