How to render content outside of a volume bounds

Add decorative content and visual effects that can display around a volume.

Overview

We can use the new preferredWindowClippingMargins view modifier in visionOS 26. This will allow our view to render more content around a volume, but there are some things to keep in mind. Like many of the modifiers in this class of features, it is important to think of this as a request. Ultimately, visionOS will determine if it is able to grant this request. Also keep in mind that content outside the bounds is not interactive. Don’t expect to use gestures or manipulation in this extra area.

Let’s add preferredWindowClippingMargins to the top level view in our volume.

VStack {
  ...
}
.preferredWindowClippingMargins(.all, 300)

This will request 300 points of extra space on all six sides of the volume. We could also provide a list of the sides we want. In this example, we’re using all sides except for the front.

struct ContentView: View {

    @State private var length: CGFloat = 300
    @State private var edges: Edge3D.Set = [.top, .leading, .bottom, .trailing, .back]

    var body: some View {
        VStack {
            RealityView { content in
            ...
            }
        }
        .preferredWindowClippingMargins(edges, length)
    }
}

It’s not clear at time of writing how much space we can request, but we’ve seen it work with 640 points on all sides.

See also: Set the scene with SwiftUI in visionOS. This session shows a great way to use this along with a new environment variable and PhysicalMetric to adapt RealityKit content based on the space available. We’ll explore a more detailed example like that soon.

Video Demo

A video demo showing clipped content being allowed to extend beyond the bounds of the volume

Example Code

Sample code is available in Garden030 in the Step Into Example Projects repo.

struct ContentView: View {

    @State private var length: CGFloat = 0
    @State private var edges: Edge3D.Set = [.top, .leading, .bottom, .trailing, .back]

    var body: some View {
        VStack {
            RealityView { content in

                if let scene = try? await Entity(named: "Scene", in: realityKitContentBundle) {
                    content.add(scene)
                }
            }
        }
        .preferredWindowClippingMargins(edges, length)
        .debugBorder3D(.white)
        .toolbar {
            ToolbarItem(placement: .bottomOrnament, content: {
                HStack {

                    HStack {
                        EdgeToggleButton(edge: .top, label: "Top", edges: $edges)
                        EdgeToggleButton(edge: .bottom, label: "Bottom", edges: $edges)
                        EdgeToggleButton(edge: .leading, label: "Leading", edges: $edges)
                        EdgeToggleButton(edge: .trailing, label: "Trailing", edges: $edges)
                        EdgeToggleButton(edge: .back, label: "Back", edges: $edges)
                    }

                    Divider()

                    Slider(value: $length, in: 0...1000, label: {
                        Text("Length")
                    })
                    .frame(width: 200)
                }
            })
        }
    }
}

struct EdgeToggleButton: View {
    let edge: Edge3D.Set
    let label: String
    @Binding var edges: Edge3D.Set
    
    var body: some View {
        Button(action: {
            if edges.contains(edge) {
                edges.remove(edge)
            } else {
                edges.insert(edge)
            }
        }, label: {
            Label(label, systemImage: edges.contains(edge) ? "circle.circle.fill" : "circle")
                .labelStyle(.titleAndIcon)
        })
    }
}

Support our work so we can continue to bring you new examples and articles.

Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?