Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

How do you restore a Sheet's window frame in SwiftUI for macOS

On macOS, it's not uncommon to present windows as sheets that can be resized. By setting the NSWindow's various frame auto save properties, you can restore the size of the sheet the next time it is presented.

When presenting a Sheet from within SwiftUI using the .sheet view modifier, how can I preserve and restore the sheet's frame size?

The closest I've been able to come is to put the SwiftUI view into a custom NSHostingController and then into an NSViewControllerRepresentable and then override viewWillAppear and look for self.view.window, which is all little awkward.

Is there a more idiomatic way to achieve this in "pure" SwiftUI?

Answered by DTS Engineer in 838899022

On macOS, it's not uncommon to present windows as sheets that can be resized. By setting the NSWindow's various frame auto save properties, you can restore the size of the sheet the next time it is presented.

Are you referring to Sheet or windows?

Currently SwiftUI sheets on macOS generally don't support resizing.

Is there a more idiomatic way to achieve this in "pure" SwiftUI?

Using NSHostingController is also perfectly acceptable. You might want to constrain up the hosting view to the window and that can allow content size changes to be reflected and resize the window.

Accepted Answer

On macOS, it's not uncommon to present windows as sheets that can be resized. By setting the NSWindow's various frame auto save properties, you can restore the size of the sheet the next time it is presented.

Are you referring to Sheet or windows?

Currently SwiftUI sheets on macOS generally don't support resizing.

Is there a more idiomatic way to achieve this in "pure" SwiftUI?

Using NSHostingController is also perfectly acceptable. You might want to constrain up the hosting view to the window and that can allow content size changes to be reflected and resize the window.

Are you referring to Sheet or windows?

Well, in AppKit I can present an NSViewController as a sheet via NSViewController.presentViewControllerAsSheet or I can present an NSWindow via NSWindow.beginSheet...

When using NSWindow, I have easy access to NSWindow.frameAutosaveName which, if set before the NSWindow is presented as a sheet, will restore it's size. If I use NSViewController, I have to grab the window in something like viewWillAppear, but that does work.

In the end, I ended up a bridged solution that does the following:

  • Embeds the SwiftUI view that should be presented as a sheet inside an NSHostingController.
  • Create a new NSWindow with the hosting controller as the contentViewController.
  • Configure the NSWindow as appropriate and then present as a sheet through AppKit.

It took quite awhile to get it right, but in the end the secret sauce was setting NSHostingController.sizingOptions to .intrinsicContentSize.

There's still an issue with NSWindow resizing when its contentViewController is set, but I think that's been an AppKit issue for awhile. It's more reliable to just add the view to the NSWindow's contentView instead, otherwise the NSWindow resizes to the initial size of the hosting controller.

But for now, it appears to be working. I suspect I'm pushing my luck a little bit because the actual view hierarchy is:

  • NSWindow.contentViewController -> NSHostingController
  • NSHostingController -> NSViewControllerRepresentable
  • NSViewControllerRepresentable -> NSSplitViewController
  • NSSplitViewItems -> SwiftUI Views

VSplitView and HSplitView don't appear to remember their positions at all. NSSplitViewController does, hence the need to use it instead. But NSSplitViewController's splitViewItems were wreaking havoc with a resizable sheet. Intrinsic content size appears to have resolved that issue.

Ultimately, it's three SwiftUI views hosted inside an NSSplitViewController which is presented as a resizable sheet.

How do you restore a Sheet's window frame in SwiftUI for macOS
 
 
Q