macosswiftuinswindow

Update NSWindow content size to fit SwiftUI view


I have a SwiftUI view that do not know the size of because of variable length strings (e.g. due to localisation). I need to have the containing NSWindow resize to fix the content. I can provide a fixed/minimum width.

Creating a new Mac app with an AppKit delegate yields a fairly simple AppDelegate:

import Cocoa
import SwiftUI

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView()

        // Create the window and set the content view.
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.isReleasedWhenClosed = false
        window.center()
        window.setFrameAutosaveName("Main Window")
        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    }
}

I then have a SwiftUI with a button that adds to the label's text to simulate changing the text:

import SwiftUI

struct ContentView: View {
    @State var text = "Hello, World!"

    var body: some View {
        VStack {
            Text(text)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            Button("Add Some Text") {
                text += "\nHello, World!"
            }
        }
    }
}

Clicking the button eventually causes too many lines of text to be added and the label truncates.

How can I update the container NSWindow's content size when the size of the SwiftUI view changes?


Solution

  • As far as I understood you need the following (tested with Xcode 12.1)

    struct ContentView: View {
        @State var text = "Hello, World!"
    
        var body: some View {
            VStack {
                Text(text)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                Button("Add Some Text") {
                    text += "\nHello, World!"
                }
            }
            .frame(minWidth:480, minHeight: 300)     // << this for default
            .fixedSize()                             // << this to update container 
        }
    }