SwiftUI: Puzzled about the window zoom behaviour


Trying to get into macOS development with SwiftUI, and relatively new to programming in general. Being a fan the zoom behaviour in macOS, decided to start with this behaviour, but stuck in understanding how I need to proceed (following lines may sound as a rant, but it is not :slight_smile:).

SwiftUI views take only the minimum amount of space they occupy, right? So logically, without any modifiers, the green zoom button should theoretically resize the window to the size of the view inside the window.

To try it out, I've created a simple application with only a Text view in it. Applied some padding and border. Still not understandable by me, zooming the window occupies the whole screen, instead of getting the bounds of the Text view. Why isn't this the standard behaviour?

To solve this issue, I get the size and the width of the Text view via GeometryReader, and apply it with the .frame modifier, but it still does not budge. The only way I can get to zoom is setting the maxWidth and maxHeight options, but then it is not possible to resize the window more than those values.

What do you think? Is this an incorrect behaviour, so that it makes sense to raise an issue? If it is not, can you please guide me through the parts of the language and the API that I need to study more? I would really like to solve this, and see it done in a proper way. I can provide some code examples to elaborate on stuff more.

Thanks for reading!

The green button is a "zoom to full-screen", and it really has two toggle states, go to full-screen and go to previous frame. Note that the three buttons are window buttons, not view buttons. This is operating system window behavior that pre-dates Swift, not to mention SwiftUI.

Just to be on the same page, full-screen behaviour is another behaviour; I am talking about the window zoom, which you press option+green button and/or double click on the title bar if set from Dock preferences.

You may be able to modify the behavior manually through an NSWindow delegate method, or an NSWindowController delegate. I have seen a macOS application go into full-screen mode and not occupy the entire screen (it didn't create a new desktop, just occupied current desktop keeping the menu bar always showing). So, I think you can manipulate the re-sizing process manually. If left to its own devices, the system will create a new "desktop" and occupy that "desktop" fully.

Extending NSWindowController and overriding windowWillUseStandardFrame(_:) gives me the ability to manipulate zoom states. But it still does not play with SwiftUI nicely by default.

Occupying the whole desktop might be an incorrect/unimplemented behaviour depending on the context and the content of the application. During Classic Mac OS times, zoom behaviour exactly took the amount of needed space to keep the application content showing, not a single pixel more. macOS native applications still play nice with this, but most 3rd party applications do not bother. Full-screen mode is overrated, zoom is the ultimate key to using screen estate effectively.

By design, SwiftUI views have the potential to play nicely with this OS feature, but it looks like it does not work out of the box. I would be really glad to see this within SwiftUI.

I see the difference. But, the implied point is still the same: it's a window control process, not a view process. You might need to look into NSWindow support as previously mentioned.

Lots of stuff does not work for SwiftUI on the Mac. I've pretty much given up using SwiftUI for anything but a simple application that works like an iOS/iPadOS application. Still has a long way to go for a "traditional" macOS application that has a UI that goes beyond a master-detail view architecture, in my opinion. For example, at this point it eludes me how I would build a CAD application using SwiftUI

1 Like

I'm also assuming you are using a Scene view and WindowGroup view to hold your text view. You may still need an AppDelete, and may need to build a workaround using NSWindowController, the above pieces, and the AppKit <-> SwiftUI functionality. Might try looking through developer.apple.com, there are examples on the forums about advanced window functionality via AppKit interfaces.

Looks like I'll have to take that route. Thanks for the suggestions.

I don't mind wrapping AppKit functionality, but still looking forward to see SwiftUI specific methods to make it more native.

The macOS documentation states that option-clicking on the green button causes the window to maximize—that is, it fills up the whole screen (or becomes as large as the window allows), but doesn't go into full-screen mode. It does not say that option-clicking on the green button causes the window to take "the amount of needed space to keep the application content showing, not a single pixel more".

When testing apps built with SwiftUI and those built with AppKit, I observed the behavior stated in the documentation. In fact, I could not find a single application (including native ones) on my computer that exhibits the behavior you described when I option-clicked on the green button. We are no longer in classic macOS times; option-clicking on the green button does not do what it used to do. SwiftUI has nothing to do with this issue.

Yes, maximise, not full-screen, as large as the window allows. My point is, why not make this "as large as the window allows" (the calculated content size) into an automatic behaviour instead of having developers calculate it, since SwiftUI views are so space efficient by default?

Invoking zoom on Safari causes the window to be resized to the website's maximum frame view (you can try with this very website, first make the window smaller, then invoke zoom). Same as Finder, when the sorting options are disabled (no grid), it will resize the window to the dimensions that are occupied by the icons (more or less). This behaviour is still also present in many third party applications including Apple's (Pages etc.) when it makes sense to include.

I don't see any difference between the Classic Mac OS behaviour and macOS', I can also guess that the zoom levels were calculated by the developers per se by then, not by the operating system. I was probably wrong above by saying that Classic Mac OS did this automatically.

Yes indeed. Preview.app is another example. Apple's developer documentation indicates that this is the correct behaviour:

The standard frame for a window should supply the size and location that are “best” for the type of information shown in the window, taking into account the available display or displays. For example, the best width for a window that displays a word-processing document is the width of a page or the width of the display, whichever is smaller. The best height can be determined similarly. On return from this method, the zoom: method modifies the returned standard frame, if necessary, to fit on the current screen.

1 Like

The documentation you link to actually doesn’t define explicitly what it means by “maximize,” and you’ll see on reading further when it talks about double-clicking that it refers to the same thing as what’s called “zoom” under System Preferences. @bitigchi’s description of that behavior is more or less accurate.

1 Like

Please try to explain again what behavior you're trying to achieve in your app when you option-click the green button. At this point, I'm very confused.

Try to option-click the green button (also called the zoom button, and maximize button) on e.g. Safari.
You'll see that it does not fill the entire screen, but takes the minimum size needed to maximize its viewing space. That is, it'll maximize vertically, but not horizontally. You'll find that this is common among Apple native apps, and some third party apps.

Swift UI should in theory be able to automatically find this "ideal" size and default the zoom button behaviour for a more "native" feel. Currently, it does not.

How? Does Appkit do this automatically?

Well, the old springs and struts, the somewhat newer auto layout and the newest SwiftUI, all uses different layout calculation mechanisms. But yeah, it could work.

SwiftUI is missing some important notions that are available in AppKit's auto-layout: the content hugging priority and content compression resistance priority. These priorities allow to perfectly describe when views should truncate their content or go larger. Also they allow to say whether views can cause the window to resize (by setting priorities below or above NSLayoutPriorityWindowSizeStayPut).

1 Like