How to use default placement to position new windows

In visionOS we can provide a default window placement. This placement will be used when an instance of a window is opened. When nothing is provided, a new window will appear slightly in front of the window. It will be positioned in front of the volume from which it was opened.

Note: The default placement value is only used when opening a new instance of a window. As of visionOS 2, there is no way for developers to move or position existing windows.

There are five placements supported so far in visionOS 2.

  • Relative to an existing window or volume
    • Above
    • Below
    • Trailing
    • Leading
  • Relative to the user
    • Utility

Relative to an existing window

If we wanted to open a window to the left of our main window we could do something like this.

// Main window
WindowGroup {
    ContentView()
}
.defaultSize(width: 500, height: 500)

WindowGroup(id: "YellowFlower") {
    Text("🌼")
        .font(.system(size: 128))
}
.defaultSize(CGSize(width: 300, height: 200))
.defaultWindowPlacement { _, context in
    if let mainWindow = context.windows.first {
        return WindowPlacement(.leading(mainWindow))
    }
    return WindowPlacement(.none)
}

We use the defaultWindowPlacement scene modifier and extract the main window from the context provided to the closure. Then we can specify the placement relative to the main window like this: WindowPlacement(.leading(mainWindow)).

The yellow flower window appeared next to the main window.

We can repeat that process for each of the other relative placements.

Examples of relative placements: above, below, leading, and trailing.

Relative to the user

A very useful option is .utilityPlacement. This will open a window closer to the user. I often use this placement for tool panels and menus for volumes and immersive spaces. This one is easier to use since we don’t need to get a proxy to another window.

WindowGroup(id: "Rocks") {
    Text("🪨🪨🪨")
        .font(.system(size: 48))
}
.defaultSize(CGSize(width: 300, height: 100))
.defaultWindowPlacement { _, context in
    return WindowPlacement(.utilityPanel)
}

WindowPlacement(.utilityPanel) is doing all the work here. When seen head-on in the simulator, you may assume this is just a lower version of the “below” placement. But when we rotate the camera a bit, you can see this window was placed much closer to the user.

visionOS simulator demo of multiple windows opening relative to a main window.

Sample code for this post is available in Garden03 in Step Into Examples on GitHub

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?