swiftmacoscocoawindowprogrammatically-created

Swift open new window programmatically in macOS


In Swift using macOS: By removing @NSApplicationMain (and making a subclass of NSWindowController) in AppDelegate I create the main window programmatically, without using storyboards, etc.:

//@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var window: NSWindow!
    var viewController: NSViewController!
    var windowController: NSWindowController!


    func configMainWindow(_ viewController: NSViewController) {
        window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
                      styleMask: [NSWindow.StyleMask.closable, NSWindow.StyleMask.titled, NSWindow.StyleMask.resizable, NSWindow.StyleMask.miniaturizable],
                      backing: NSWindow.BackingStoreType.buffered,
                      defer: false)
        window.title = "My App"
        window.setFrameAutosaveName("My App")
        window.center()
        window.isOpaque = false
        window.isMovableByWindowBackground = true
        window.backgroundColor = NSColor.white
        window.makeKeyAndOrderFront(nil)
        window.contentViewController = viewController
        windowController = WindowController(window: window)
    }


    func applicationDidFinishLaunching(_ aNotification: Notification) {
        viewController = ViewController()
        configMainWindow(viewController)
    }
}

The windowController attaches a toolbar, statusBar and menuBar: (Only the menuBar is loaded from a NIB. A class MainMenuAction handles the menu choices.)

class WindowController: NSWindowController, NSWindowDelegate {

    var toolbarController = ToolbarController()
    var statusBarController = StatusBarController()
    var mainMenuAction: MainMenuAction?


    override init(window: NSWindow?) {
        super.init(window: window)
        window?.toolbar = toolbarController.toolbar
        window?.delegate = self

        var topLevelObjects: NSArray? = []
        Bundle.main.loadNibNamed("MainMenu", owner: self, topLevelObjects: &topLevelObjects)
        NSApplication.shared.mainMenu = topLevelObjects?.filter { $0 is NSMenu }.first as? NSMenu
        self.mainMenuAction = MainMenuAction.shared
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


    override func windowDidLoad() {
        if let window = window {
            if let view = window.contentView {
                view.wantsLayer = true
                window.titleVisibility = .hidden
                window.titlebarAppearsTransparent = true
                window.backgroundColor = .white
            }
        }
    }
}

Additionally, I needed to add a main.swift file: (thanks for reminding me, apodidae)

let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

I tried:

    let vc = NSViewController()
    let win = configWindow(vc, windowWidth: 420, windowHeight: 673)
    let wc = NSWindowController(window: win)
    wc.window?.present
    vc.view.window?.contentViewController = vc

Where I copied the method configMainWindow from AppDelegate, to create configWindow that allowed me the specify size and the vc.


But how can I open a new window (from some method in a new class - programmatically) with a custom size and style?

Please provide a code example.


Solution

  • The following demo creates a second window called by a method in a custom class. It may be run in Xcode by adding a 'swift.main' file and replacing AppDelegate with the following code:

    import Cocoa
    
    class Abc : NSObject {
    var panel: NSPanel!
    
    func buildWnd2() {
     let _panelW : CGFloat = 200
     let _panelH : CGFloat = 200
    
     panel = NSPanel(contentRect:NSMakeRect(9300, 1300, _panelW, _panelH), styleMask:[.titled, .closable, .utilityWindow],
      backing:.buffered, defer: false)         
     panel.isFloatingPanel = true
     panel.title = "NSPanel"
     panel.orderFront(nil)
    }
    }
    
    let abc = Abc()
    
    class AppDelegate: NSObject, NSApplicationDelegate {
    var window:NSWindow!
    
    @objc func myBtnAction(_ sender:AnyObject ) {
     abc.buildWnd2()
    }
    
    func buildMenu() {
     let mainMenu = NSMenu()
     NSApp.mainMenu = mainMenu
     // **** App menu **** //
     let appMenuItem = NSMenuItem()
     mainMenu.addItem(appMenuItem)
     let appMenu = NSMenu()
     appMenuItem.submenu = appMenu
     appMenu.addItem(withTitle: "Quit", action:#selector(NSApplication.terminate), keyEquivalent: "q") 
    }
        
    func buildWnd() {
        
    let _wndW : CGFloat = 400
    let _wndH : CGFloat = 300
    
     window = NSWindow(contentRect:NSMakeRect(0,0,_wndW,_wndH),styleMask:[.titled, .closable, .miniaturizable, .resizable], backing:.buffered, defer:false)
     window.center()
     window.title = "Swift Test Window"
     window.makeKeyAndOrderFront(window)
    
    // **** Button **** //
    let myBtn = NSButton (frame:NSMakeRect( 100, 100, 175, 30 ))
     myBtn.bezelStyle = .rounded
     myBtn.autoresizingMask = [.maxXMargin,.minYMargin]
     myBtn.title = "Build Second Window"
     myBtn.action = #selector(self.myBtnAction(_:))
     window.contentView!.addSubview (myBtn)
    
    // **** Quit btn **** //
    let quitBtn = NSButton (frame:NSMakeRect( _wndW - 50, 10, 40, 40 ))
     quitBtn.bezelStyle = .circular
     quitBtn.autoresizingMask = [.minXMargin,.maxYMargin]
     quitBtn.title = "Q"
     quitBtn.action = #selector(NSApplication.terminate)
     window.contentView!.addSubview(quitBtn)
    }
     
    func applicationDidFinishLaunching(_ notification: Notification) {
     buildMenu()
     buildWnd()
    }
    
    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
     return true
    }
    
    }
    let appDelegate = AppDelegate()
    
    // **** main.swift **** //
    let app = NSApplication.shared
    app.delegate = appDelegate
    app.setActivationPolicy(.regular)
    app.activate(ignoringOtherApps:true)
    app.run()