How to control the size of windows in visionOS

Discover four methods to size windows in visionOS. Examples include defaultSize, contentSize with windowResizability and frame, and fixed size.

Setting default size

Windows in visionOS open with an initial size of 1280×720. If we want to adjust this size we can use the defaultSize scene modifier.

WindowGroup(id: "YellowFlower") {
    YellowFlowerView()
}
.defaultSize(CGSize(width: 600, height: 600))

Now, when we open the Yellow Flower window, it’s initial size will by 600×600. The user can adjust the size of this window with the drag bars. Our SwiftUI views will adapt to the new size.

A visionOS window with a default size of 600 by 600

Content Size

What if we want our window to be sized for view content instead? We can do this by adding a frame modifier to the view, then using .contentSize for the windowResizability scene modifier.

WindowGroup(id: "YellowFlower") {
    YellowFlowerView()
        .frame(minWidth: 500, maxWidth: 700, minHeight: 500, maxHeight: 700)
}
.windowResizability(.contentSize)
.defaultSize(CGSize(width: 600, height: 600))

I still like to specify a default size when I use this option. Without it, visionOS will open the window at the largest available size (based on my testing in visionOS 2). We’ve set the default size to 600×600 and provided a min and max value for the frame.

A window with a default size is resized by a user.

Fixed Size

If needed, we can force a fixed-size window by setting the frame width and height.

WindowGroup(id: "YellowFlower") {
    YellowFlowerView()
        .frame(width: 600, height: 600)
}
.windowResizability(.contentSize)
.defaultSize(CGSize(width: 600, height: 600))

Fixed-size windows may not be great for dynamic content, but they could be good for utilities or tool panels.

Resize window by resizing content

When using .windowResizability(.contentSize) the window size is based on the content size of our views. If we adjust the frame of our content, then the host window will also resize.

struct RedFlowerView: View {
    @Environment(\.dismissWindow) var dismissWindow

    @State private var widthValue: CGFloat = 600
    @State private var heightValue: CGFloat = 400
    @State private var toggle: Bool = false

    var body: some View {
        VStack(spacing: 24) {
            Text("Red Flower 🌺")
                .font(.extraLargeTitle2)

            Button(action: {
                dismissWindow(id: "RedFlower")
            }, label: {
                Label("Close Window", systemImage: "xmark.circle")
            })
        }
        .padding()
        .frame(width: widthValue, height: heightValue)
        .onAppear {
            Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in
                toggle.toggle()
                withAnimation(.easeInOut(duration: 2.0)) {
                    widthValue = toggle ? 400 : 600
                    heightValue = toggle ? 600 : 400
                }
            }
        }
    }
}

A new window is opened and resized programmatically

Sample code is available in Garden02 in Step Into Example Projects

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?