swiftmacosswiftuicocoa

How to assign ContentView() to NSWindow?


I'm using Cocoa to create a window. I want to display the ContentView() in that window.

import Cocoa

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    var windowX: NSWindow!
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Desired window dimensions
        let windowWidth: CGFloat = 550
        let windowHeight: CGFloat = 710
        
        // Get the screen size (optional, for centering the window)
        let screenRect = NSScreen.main?.frame ?? NSRect.zero
        let windowRect = NSRect(
            x: (screenRect.width - windowWidth) / 2,
            y: (screenRect.height - windowHeight) / 2,
            width: windowWidth,
            height: windowHeight
        )
        
        // Create the window with desired style masks
        windowX = NSWindow(
            contentRect: windowRect,
            styleMask: [.titled, .miniaturizable],
            backing: .buffered,
            defer: false
        )
        
        windowX.title = "App Window"
        
        let contentView = NSView(frame: NSRect(x: 0, y: 0, width: windowWidth, height: windowHeight))
                contentView.wantsLayer = true // Enable layer-backed view
                
        windowX.contentView = NSHostingView(rootView: ContentView())
    }
    
    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }
}

Any ideas how To make it work? When the app loads, it is just empty. There is nothing to show, or display. And it doesn't work like it is. I'm not sure why. Can you offer some advice?


Solution

    1. create AppDelegate
    class AppDelegate: NSObject, NSApplicationDelegate {
        var mainWnd: NSWindow?
        private var mainWndController: NSWindowController?
    
        func applicationDidFinishLaunching(_ aNotification: Notification) {
            openMainWnd()
        }
    }
    
    1. attach appDelegate using NSApplicationDelegateAdaptor to your SwiftUI app. Also you need to create at least 1 scene. To be sure that this scene will not be shown on app start you need to create Settings scene
    import SwiftUI
    
    @main
    struct FocusitoApp: App {
        @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
        
        var body: some Scene {
            Settings {
                SettingsView()
            }
            .commands {
                ReplaceMenus()
            }
        }
    }
    
    1. in function openMainWnd() you need to
    
        func openMainWnd(show: Bool = true) {
            if mainWndController == nil {
                let wnd = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 200, height: 200),
                                  styleMask: [.closable, .titled],
                                  backing: .buffered,
                                  defer: true)
                
                wnd.contentView = NSHostingView(rootView: MainView() )
                
                // all of your customization
                
                self.mainWnd = wnd
                self.mainWndController = NSWindowController(window: wnd)
            }
        }
    

    Bonus

    If you will need to customize NSWindow behavior you can use code similar to following:

    class NSWindowCust: NSWindow {
        override func close() {
            //my custom logic
            super.close()
        }
    }
    

    and to use NSWindowCust in AppDelegate instead of NSWindow