I’m building a visionOS app that starts with a regular 2D SwiftUI window. From that 2D window, the user can enter a volumetric mode, where I want to open a large volumetric WindowGroup and have it appear centered around the same spatial position as the original 2D window.
The volumetric window is physically large, roughly over 1m × 1m × 30cm. Because of that, placement behavior is very noticeable.
My intended behavior is:
- User is interacting with a regular 2D window.
- User taps a button.
- A large volumetric window opens.
- The volumetric window appears in front of the user, ideally centered on or near the original 2D window’s position.
- The original 2D window is dismissed or replaced.
My current workaround is to call openWindow(id:) for the volumetric window, then dismiss the original 2D window. This works in the sense that the volume is created, but its initial position is noticeably offset from the original 2D window.
I also tried using defaultWindowPlacement to control the placement of the volumetric window relative to the existing 2D window. I tested placements such as .below, .trailing, and other relative positions.
However, because the volumetric window is large, the result is worse: when I open the volume from the 2D window, the volumetric window appears to move instantly far away from the user’s view, almost as if it flies out of the visible workspace. After that, I can no longer see or interact with the volume.
Interestingly, if I then go back to the system Home View and tap the app icon again, the volumetric window appears normally in front of the user.
Here is a simplified version of my setup:
@main struct MyApp: App { var body: some Scene { WindowGroup(id: "main") { MainWindowView() } WindowGroup(id: "volume") { VolumeView() } .windowStyle(.volumetric) .defaultSize(width: 1.0, height: 1.0, depth: 0.3, in: .meters) // I also tried defaultWindowPlacement here, // using placements such as .below, .trailing, etc. } } struct MainWindowView: View { @Environment(.openWindow) private var openWindow @Environment(.dismissWindow) private var dismissWindow var body: some View { Button("Open Volume") { openWindow(id: "volume") dismissWindow(id: "main") } } }
What I would like to know:
- Is there a supported way to open a large volumetric window from a 2D window while preserving or approximating the 2D window’s spatial center?
- Is defaultWindowPlacement expected to work reliably for large volumetric windows, or can relative placements such as .below or .trailing cause the volume to be placed outside the user’s comfortable visible area?
- Is there any API that exposes the current 2D window’s spatial position or center so I can place the volumetric window more precisely?
- Can pushWindow(id:) be used to replace a 2D window with a volumetric window while preserving placement, or is this transition not currently supported?
- Why would the same volumetric window appear far away when opened from the 2D window, but appear normally in front of the user when the app is reopened from the system Home View?
- What is the recommended UX or technical pattern for transitioning from a regular 2D window into a large volumetric window without the volume jumping or appearing outside the user’s view?
I’m testing this on:
- visionOS version: [26.5]
- Xcode version: [26.4.1]
- Device or Simulator: [vision pro m2 & m5]
- SwiftUI app lifecycle
- Source scene: regular WindowGroup
- Destination scene: WindowGroup with .windowStyle(.volumetric)
- Approximate volume size: over 1m × 1m × 30cm
Any guidance on the recommended placement strategy for large volumetric windows would be appreciated.
Hello @Sakuya_Izayoi , thank you for your question!
In general, developers have limited control over where the system places the first window for your app.
The system will attempt to place the first window in front of the person wearing the device, although you can place other windows relative to existing open windows, as you have discovered with the defaultWindowPlacement(_:) API.
To answer your questions directly:
-
No, there is no public API to center a volumetric window on an existing window. Window placement on visionOS works semantically using APIs like
.trailing(window),.utilityPanel, etc. -
defaultWindowPlacement(_:)will align your volume to the edge of the reference window. If your reference window is one meter wide, and your volume configured with.trailing(window)is one meter wide, the system will place the volume 1 meter to your right when it opens. -
No. The system and the user control window placement, so you will need to use
defaultWindowPlacement(_:) -
No,
pushWindowrequires that both views be the same window style. -
The system will attempt to place the first window in an app session in front of the user. When your volume is the first window, the system will ignore
defaultWindowPlacementand place the window in front of the user. When you open your volume from another window in your app (so your volume is no longer the first window), then the system will respectdefaultWindowPlacementand position the volume relative to the reference window. -
You have a couple of options.
.utilityPanelmay get you close to what you want: it will place the volume relative to the person using your app, somewhere they can easily reach. Alternatively, you may want to reconsider the UI flow: open the volume first, and spawn the 2D window inside the volume. This way, you will have direct control over the position of the 2D window inside the volume. If your volume has content, you can simply hide the content while the 2D window is active.
You can also send us feedback using Feedback Assistant, and I strongly encourage you to do so if you feel like there is still functionality that you are missing.