WWDC Quick Look 💓 By SwiftGGTeam
What’s new in visionOS 26

What’s new in visionOS 26

Watch original video

Highlight

visionOS 26 elevates the volume to a first-class citizen: it can present sheets, spill beyond its bounds, and let 3D objects follow hand gestures to rotate — all with a single view modifier.


Core Content

Since the launch of Vision Pro last year, the pitfall developers have hit most often is that a volume behaves like a “sealed box”. 3D content inside the container renders fine, but everyday UI such as alerts, sheets, and menus do not work inside a volume; geometry that exceeds the window bounds gets clipped; and rotation gestures for 3D objects require writing an entire custom stack. The result is that every volumetric app ends up reinventing the wheel, with uneven experiences.

visionOS 26 tears these pain points down one by one. Edwin opens by stating the guiding principle: let SwiftUI’s existing 2D layout tools extend naturally into 3D. depthAlignment lets VStack align along the front-to-back axis; rotation3DLayout brings rotation into the layout system; preferredWindowClippingMargins allows content to spill beyond the bounds; and the Object Manipulation API replaces a custom gesture stack with a single .manipulable() modifier. The system also unifies the coordinate spaces of SwiftUI, RealityKit, and ARKit, so developers writing RealityKit no longer need to route everything through RealityView — they can attach SwiftUI gestures and attachments directly to an entity. Taken together, these changes finally let the volume carry a full app as its main scene.

Detailed Content

SwiftUI layout enters the third dimension (02:25)

// Layout types back align views by default

struct LandmarkProfile: View {

    var body: some View {
       VStackLayout().depthAlignment(.front) {
            ResizableLandmarkModel()
            LandmarkNameCard()
        }
    }
}

Key points:

  • VStackLayout() is the familiar vertical stack; it previously only worked on the X/Y plane.
  • .depthAlignment(.front) aligns all child views to the same front plane along the Z axis; the 3D model can sit deeper, and the name card automatically snaps to the front of the model, significantly improving readability.
  • This is a layout-level modifier — no manual offset calculation required.

Content overflow inside a volume (04:22)

// Dynamic Bounds Restrictions

struct ContentView: View, Animatable {

    var body: some View {
        VStackLayout().depthAlignment(.front) {
            // . . .
        }
        .preferredWindowClippingMargins(.all, 400)
    }
}

Key points:

  • preferredWindowClippingMargins(.all, 400) tells the system: allow content to spill 400 points in every direction.
  • The purpose is to let virtual objects “peek out” of the window — for example, a mountain peak protruding from the volume or a waterfall crossing the boundary — without needing to enlarge the volume dimensions for that.
  • This is a preference; the system will clip dynamically based on distance and occlusion when needed.

Object Manipulation: one modifier does it all (05:05)

// Apply the manipulable view modifier to each Model3D block per 3D object

struct RockView: View {
    var body: some View {
        RockLayout {
            ForEach(rocks) { rock in
                Model3D(named: rock.name, bundle: realityKitContentBundle) {
                    model in
                    model.model?
                        .resizable()
                        .scaledToFit3D()
                }
                .manipulable()
            }
        }
    }
}

Key points:

  • .manipulable() gives Model3D automatic support for two-handed grab, rotate, and scale, with physical intuition consistent with real-world objects.
  • No need to write a DragGesture + RotateGesture3D combination, nor to manually handle two-handed coordination.
  • For those using RealityKit, calling ManipulationComponent.configureEntity(rock) provides the same behaviour; USDZ models previewed in Quick Look get this capability for free.

SwiftUI gestures attach directly to an Entity (06:36)

// Gestures on entities
struct GestureExample: View {
    @GestureState private var dragMountain: Float = 0
    @GestureState private var dragTerrain: Float = 0
			var body: some View {
        RealityView { content in
            let drag1 = GestureComponent(
                DragGesture().updating($dragMountain) { value, offset, _ in
                    offset = Float(value.translation.width)
                })
            let drag2 = GestureComponent(
                DragGesture().updating($dragTerrain) {evalue, offset, _ in
                    offset = Float(value.translation.width)
                })
            mountain.components.set(drag1)
            terrain.components.set(drag2)
        } update: { content in
            // . . .
        }
    }
}

Key points:

  • GestureComponent packages SwiftUI’s DragGesture directly onto an entity, eliminating the need for RealityView to act as an intermediary forwarding hit-testing.
  • Each entity can carry its own independent gesture without interfering with others; previously this required a long chain of targetedToEntity branching logic.
  • @GestureState still follows SwiftUI’s state management, so existing code can be ported across as-is.

Look to Scroll: scroll with a glance (33:45)

// SwiftUI
var body: some View {
    ScrollView {
        HikeDetails()
    }
    .scrollInputBehavior(.enabled, for: .look)
}


// UIKit
let scrollView: UIScrollView = {
    let scroll = UIScrollView()
    scroll.lookToScrollAxes = .vertical
    return scroll
}()

Key points:

  • In SwiftUI, adding a single line scrollInputBehavior(.enabled, for: .look) lets the user trigger scrolling by moving their gaze to the edge, hands-free.
  • On the UIKit path, use lookToScrollAxes, specifying vertical, horizontal, or both as needed.
  • Reading apps and long-content browsing apps are the first beneficiaries; games should be cautious to avoid conflicting with aiming operations.

Other key updates

  • Foundation Models framework (10:53): Access on-device LLMs, with support for guided generation and tool calling.
  • Spatial Audio Experience API (08:26): Each window/volume gets its own spatialised audio source, which can migrate seamlessly between scenes.
  • Environment Occlusion (09:04): Use EnvironmentBlendingComponent to let virtual objects be occluded by real static objects.
  • Nearby Window Sharing and shared ARKit world anchors: Multiple people in the same room can interact with the same volume; existing SharePlay code works without changes to gain the nearby experience, and adding an isNearbyWithParticipant check enables finer differentiation.
  • Spatial Browsing and HTML <model>: Embed USDZ models directly in web pages, and enter a spatial reading mode when browsing long articles.
  • Enterprise APIs: Protected Content is a single modifier that blocks screenshots, screen recording, and AirPlay; Window Follow Mode makes the window follow the user as they move around; Return to Service is used for secure reset when devices are shared.

Core Takeaways

  • Treat the volume as the main stage and the window as the accessory: Previously you used a window as the main body and a volume as a 3D attachment; after visionOS 26 the reverse makes more sense. Volumes can now present sheets, spill beyond bounds, and spatialize audio, so primary interaction can live entirely inside them.

    • Why it is worth doing: It gives the app spatial presence from the start, preventing users from failing to perceive what makes Vision Pro different while stuck in a 2D window.
    • How to start: Change the first scene to WindowGroup(.volumetric), add depthAlignment(.front) to lay out the main elements, and use preferredWindowClippingMargins to let key objects peek beyond the boundary.
  • Delete your custom 3D gesture stack and replace it entirely with .manipulable() / ManipulationComponent: Gesture coordination, two-handed pinch-to-zoom, and inertial snap-back are details Apple has already unified at the system level; it is extremely hard to achieve that consistency on your own.

    • Why it is worth doing: Users have already built consistent muscle memory in Quick Look, the system Photos app, and other third-party apps; custom gestures break that mental model.
    • How to start: Replace existing DragGesture3D + RotateGesture3D combinations; on the RealityKit side, call ManipulationComponent.configureEntity(_:) once per interactive entity during the RealityView make phase.
  • Media apps should prioritise APMP (Apple Projected Media Profile) support: Footage shot on Canon, GoPro, and Insta360 in 180/360-degree and wide-angle formats now has unified metadata, so after import it can go straight through the system immersive playback pipeline.

    • Why it is worth doing: Users are importing more and more footage from external cameras; APMP saves you from writing special-case handling for every lens type.
    • How to start: Check whether your upload pipeline preserves original metadata; on the player side, use the system-provided immersive player and let projection information drive rendering.
  • Enterprise apps should apply Protected Content by default: Views handling finance, healthcare, or design review have screenshots and AirPlay as high-risk compliance points.

    • Why it is worth doing: A single view modifier closes an entire class of compliance issues with zero business-logic changes.
    • How to start: Find the root view that displays sensitive data and add the Protected Content modifier; evaluate whether external sharing use cases also need Window Follow Mode for further constraints.

Comments

GitHub Issues · utterances