Spatial SwiftUI: Using presentations in Volumes

Starting in visionOS 26 we can now use SwiftUI pickers and popovers in volumes.

Overview

If you’ve been working with Volumes for a while you have likely run into this:

Presentations are not currently supported in Volumetric contexts.

or maybe this:

Presentations are not permitted within volumetric window scenes.

We took a look at some alternatives using visionOS 2.

Spatial SwiftUI: Volumetric presentation with attachments

Spatial SwiftUI: Building volumetric pickers with attachments

I’m happy to say that we can put those days behind us. visionOS 26 brings presentation support to volumes. This includes SwiftUi views in ornaments, toolbars, and RealityView attachments.

This is a simple example of three common presentations: Sheet, Popover, and Picker. I used the same view in both an ornament and an attachment.

A video demo of popover, picker, and sheet presentations in a visionOS volume.

  1. Sheets: these will work, but we don’t have control of where the sheet content will display. If you look at the end of the video demo, you can see the sheet moving as I move closer to the volume. I don’t recommend using sheets in volumes. These are more suitable for modal state in windows. If you must use a modal sheet in a volume, consider hiding the other volume content while the sheet is presented.
  2. Popovers: These give us a bit more flexibility of placement. We can adjust both two values to dial in placement.
    • attachmentAnchor where the sheet is anchored related to the parent
    • arrowEdge the edge of the popover content o align with the attachment anchor
  3. Pickers: these work great. From my limited testing, these seem to be anchored relative to the parent control/button.

We can also use the new Presentation Component in RealityKit. This lets us show popovers relative to an entity transform.

Make sure to keep your volume size and bounds in mind as you design ornaments and attachments. It can take a bit of trial and error to make sure your presentations are not being clipped.

Example Code

I’ve barely scratch the surface and presentations in volumes. This was just a quick test to see how things behave in the visionOS 26 beta.

struct Example085: View {

    var body: some View {
        RealityView { content, attachments in

            guard let scene = try? await Entity(named: "SwiftUIScienceLab", in: realityKitContentBundle) else { return }
            content.add(scene)
            scene.position.y = -0.3
            scene.scale = .init(repeating: 0.7)


            if let panel = attachments.entity(for: "AttachmentContent") {
                panel.position = [0, -0.1, 0.1]
                content.add(panel)
            }

        } attachments: {
            Attachment(id: "AttachmentContent") {
                FormView(title: "Attachment Presentations")
            }
        }
        .ornament(attachmentAnchor: .parent(.topBack), ornament: {
            FormView(title: "Ornament Presentations")
        })
    }
}

fileprivate struct FormView: View {

    var title: String = "Volume Presentations!"

    @State private var showingSheet = false
    @State private var showingPopover = false
    @State private var someDate = Date()

    var body: some View {
        Form() {

            Text(title)
                .font(.largeTitle)

            Button("Show Sheet", action: {
                showingSheet.toggle()
            })

            Button("Show Popover", action: {
                showingPopover.toggle()
            })

            DatePicker("Date Picker",
                       selection: $someDate,
                       displayedComponents: .date
            )

        }
        .sheet(isPresented: $showingSheet) {
            VStack {
                Text("Sheets show up somewhere in the volume, relative to the user")
                    .padding()
                Button("Close Sheet", action: {
                    showingSheet = false
                })
            }
        }
        .popover(isPresented: $showingPopover, attachmentAnchor: .point(.trailing), arrowEdge: .leading, content: {
            VStack {
                Text("Popovers")
                    .padding()
                Button("Close Popover", action: {
                    showingPopover = false
                })
            }
        })
        .frame(width: 400, height: 300)
        .glassBackgroundEffect()
    }
}

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?