Search code examples
swiftxcodemacoscocoaswift4

Cocoa Xcode: Open Window Controller from AppDelegate Programmatically using Swift 4 for Mac?


I am making a simple menu bar app which has 2 items - Preferences & Quit

I want to open a new Window when I click on Preferences

Currently I have

func applicationDidFinishLaunching(_ aNotification: Notification) {
        constructMenu()
    }

func constructMenu() {
        let menu = NSMenu()

        menu.addItem(NSMenuItem(
                       title: "Preferences...", 
                       action: #selector(AppDelegate.preferencesWindow(_:)), 
                       keyEquivalent: "P"))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(
                       title: "Quit", 
                       action: #selector(NSApplication.terminate(_:)), 
                       keyEquivalent: "q"))

        statusItem.menu = menu
    }

@objc func preferencesWindow(_ sender: Any) {
        print("open preference window here")
        if let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil) {
            let controller = storyboard.instantiateControllerWithIdentifier("preferencesWindow")
 as NSViewController

            if let window = NSApplication.shared.mainWindow {
                window.contentViewController = controller // just swap
            }
        }
    }

But it throws error on this line

if let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil) {

stating

Initializer for conditional binding must have Optional type, not 'NSStoryboard'

When I click Preferences the print statement gets logged but I don't know how to open Window programatically.

I have just dragged Window Controller from Object Library & given StoryBoard ID a value of preferencesWindow

I also tried the following because of the above error

@objc func preferencesWindow(_ sender: Any) {
        print("open preference window here")
        let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
        let controller = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "preferencesWindow")) as! NSViewController

        if let window = NSApplication.shared.mainWindow {
            window.contentViewController = controller // just swap
        }
    }

but it only logs & never opens a window. What should I do?


Solution

  • I find answers fast when I post a question on SO. Here's what worked for me

    @objc func preferencesWindow(_ sender: Any) {
            var myWindow: NSWindow? = nil
            let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"),bundle: nil)
            let controller = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "preferencesWindow")) as! NSViewController
            myWindow = NSWindow(contentViewController: controller)
            NSApp.activate(ignoringOtherApps: true)
            myWindow?.makeKeyAndOrderFront(self)
            let vc = NSWindowController(window: myWindow)
            vc.showWindow(self)
        }