Define macOS window size using SwiftUI
At times, the behaviour can be confusing. That is because once you run your app at least once, if you then manually resize and reposition your window, the sizes specified in the delegate will no longer matter.
Applications remember when a user has resized the window and will use the information stored in the UserDefaults instead, under the key "NSWindow Frame Main Window". If you would like to reset it, you need to wipe it out with the defaults command.
Now that that is out of the way, the reason why your window was so narrow is as follow:
With SwiftUI, not all views are created equal. For example: Text() is humble. It will only take as much space as needed. While other views, such as Spacer(), will expand as much as their parents offer (I call them greedy).
In your case, you have a VStack, with Spacer() in it. This means that the VStack fill expand to fill the height offered by its parent. In this case, the 300 pt from the delegate (or whatever is stored in the UserDefaults).
On the other hand, since you do not have any Spacer() inside a HStack, the ContentView will only expand horizontally to what it needs. That is, as wide as the widest Text() view. If you add HStack { Spacer() }
inside the VStack, your content view will expand to occupy the 480 pt specified in the delegate (or whatever is stored in the UserDefaults). No need to set a frame().
The other approach (specifying a frame for the ContentView), is basically telling your ContentView to be 480x300, no matter what. In fact, if you do so, you will not be able to resize the window!
So now you know and I think it is clear... but, here's something that can be very useful to you:
There's another greedy view that may assist you in debugging your window sizes: GeometryReader. This view will always take as much as offered. Run this example, and you will know exactly how much space is offered at app launch:
struct ContentView : View {
var body: some View {
GeometryReader { geometry in
VStack {
Text("\(geometry.size.width) x \(geometry.size.height)")
}.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
I have written an extensive article about GeometryReader, I recommend you check it out: https://swiftui-lab.com/geometryreader-to-the-rescue/
By the way, my AppDelegate looks like this:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: ContentView())
window.makeKeyAndOrderFront(nil)
}
UPDATE (macOS Catalina - Beta 3)
Since the Beta3, the initial ContentView of a new project, uses maxWidth and maxHeight. A clever alternative.
struct ContentView : View {
var body: some View {
Text("Hello World")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
How do I set the minimum window size with swiftUI, and how do I make the window use the specified size and position when launching?
If you created project from template for macOS SwiftUI based, then all changes you need to do is in AppDelegate.swift.
The size of window is content-defined, so you need to specify root content view frame, and to disallow window position saving you need to remove setFrameAutosaveName
, as a result your AppDelegate should look like the following
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
.frame(minWidth: 800, maxWidth: .infinity, minHeight: 500, maxHeight: .infinity)
// Create the window and set the content view.
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 800, height: 500),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.contentView = NSHostingView(rootView: contentView)
window.makeKeyAndOrderFront(nil)
}
...
Update: for SwiftUI life-cycle approach is the same - set frame to content view in window scene, like
var body: some Scene {
WindowGroup {
ContentView()
.frame(minWidth: 800, maxWidth: .infinity, minHeight: 500, maxHeight: .infinity)
}
}
How can make my Window size be big as Screen of mac?
let screenSize = NSScreen.main?.frame.size ?? CGSize(width: 800, height: 600)
Color.purple
.frame(width: screenSize.width, height: screenSize.height)
SwiftUI window size for document based MacOs App
Try the following (as window by default has .fullSizeContentView)
var body: some Scene {
WindowGroup {
ContentView()
.frame(width: 1200, height: 800) // << here !!
.frame(minWidth: 800, maxWidth: .infinity,
minHeight: 600, maxHeight: .infinity)
}
}
SwiftUI on MacOS: How do I fix the window size
I believe you need to set your maxWidth on the containing ZStack.
ZStack {
...
}.frame(maxWidth: 600)
Related Topics
How to Succinctly Get the First 5 Characters of a String in Swift
Swift: Loop Over Array Elements and Access Previous and Next Elements
Masking Uiview/Uiimageview to Cutout Transparent Text
Swift Protocol Generic as Function Return Type
Firebase Firestore Not Updating Email Verification Status
Differences Between "Static Var" and "Var" in Swift
How to Setup a Second Component with a Uipickerview
Getting Path for Resource in Command Line Tool
Using Swift to Disable Sleep/Screen Saver for Osx
Why Does My Version of Filter Perform So Differently Than Swifts
Pause an Skaction in Spritekit with Swift
How to Set Countdowntimer Mode in Datepicker on Swiftui
Enum with Identical Cases Names with Associated Values of Different Types