How to read and use volume clipping margins
Allow SwiftUI or RealityKIt content to take advantage of extra space around a volume.
We can use preferredWindowClippingMargins to request a margin. We can read the actual value directly from the environment using windowClippingMargins.
@Environment(\.windowClippingMargins) private var windowClippingMargins
...
windowClippingMargins.topThis will return values SwiftUI points (CGFloat). We could use that value directly to adapt SwiftUI views. If we’re working with RealityKit, we can use Physical Metric to get a scaler we use for our entities.
@PhysicalMetric(from: .meters) private var points = 1// When no margins, we want scale 0.5 (default)
// When margins exist, scale up to fill the extra space
let baseScale: Float = 0.5
// Note: We using the same margin on all sides, so just read one of them
let margin = windowClippingMargins.top
// Scale up based on available margin space
let marginScale = Float(margin / points)
let scaleFactor = baseScale + marginScale
scene.scale = .init(repeating: scaleFactor)In this example, we are scaling a scene to fit in a volume and fill the clipping margin. This is just one way to use this value. We can also read the value on each side of the volume and adapt accordingly. We could show and hide additional entities and visual effects, hide ornaments and controls, etc.
Video Demo
Example Code
Sample code is available in Garden030 in the Step Into Example Projects repo.
struct ContentView: View {
@Environment(\.windowClippingMargins) private var windowClippingMargins
@PhysicalMetric(from: .meters) private var points = 1
@State private var length: CGFloat = 0
@State private var edges: Edge3D.Set = [.top, .leading, .bottom, .trailing, .back]
@State private var shouldScaleContent = false
var body: some View {
VStack {
RealityView { content in
if let scene = try? await Entity(named: "Scene", in: realityKitContentBundle) {
content.add(scene)
}
} update: { content in
if let scene = content.entities.first {
if(!shouldScaleContent) {
scene.scale = .init(repeating: 0.5)
return
}
// When no margins, we want scale 0.5 (default)
// When margins exist, scale up to fill the extra space
let baseScale: Float = 0.5
// We using the same margin on all sides, so just read one of them
let margin = windowClippingMargins.top
// Scale up based on available margin space
let marginScale = Float(margin / points)
let scaleFactor = baseScale + marginScale
scene.scale = .init(repeating: scaleFactor)
}
}
}
.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()
// 640 seems to be the max as of visionOS 26 beta 6
Slider(value: $length, in: 0...640, label: {
Text("Length")
})
.frame(width: 200)
Button(action: {
shouldScaleContent.toggle()
}, label: {
Label("Scale", systemImage: shouldScaleContent ? "circle.circle.fill" : "circle")
.labelStyle(.titleAndIcon)
})
}
})
}
}
}Support our work so we can continue to bring you new examples and articles.

Follow Step Into Vision