Wendell
Wendell

Reputation: 546

How to Make macOS App Window Hidden When Closed and Reopened With Menu Bar Item?

I am developing a macOS app (using Swift & Storyboard) which window behaves like the Adobe Creative Cloud app. And I could not find the optimal solution after hours of research.

This means:

  1. When the app launches, the main window shows up with various menus on the status bar, an icon appears in the dock, and an icon appears in the status bar.
  2. When the user clicks the red X, the main window and the icon in the dock are hidden.
  3. The main app window can be reopened by clicking the status bar icon. And the dock icon reappears.

My storyboard looks like this: enter image description here

I have tried the following:

  1. By setting Application is agent (UIElement) to YES, I was able to close the main app window while keeping the app alive. However, the app icon does not show up in the dock, and there are no menus in the left side of the status bar.

  2. I was able to launch a new app window by clicking the status bar icon. But doing so simply opens a whole new window regardless of whether a window is already being presented (I only want one window to show up).

let storyboard = NSStoryboard(name: "Main", bundle: nil)
guard let window = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "main")) as? WindowController else { return }
window.showWindow(self)

Much appreciation for anyone who can help!

Upvotes: 4

Views: 2154

Answers (1)

Ely
Ely

Reputation: 9131

Don't use the Application is agent approach, but change the activationPolicy of the NSApp.

To dynamically hide the icon after closing the (last) window use this in your AppDelegate:

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
    NSApp.setActivationPolicy(.accessory)
    return false
}

And use something simular to this to initialise your menubar icon and activate the window including a dock icon:

class ViewController: NSViewController {

    var status: NSStatusItem?

    override func viewDidLoad() {
        super.viewDidLoad()

        status = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        status?.button?.title = "Test"
        status?.button?.action = #selector(activateWindow(_:))
        status?.button?.target = self
    }

    @IBAction func activateWindow(_ sender: AnyObject) {
        NSApp.setActivationPolicy(.regular)
        DispatchQueue.main.async {
            NSApp.windows.first?.orderFrontRegardless()
        }
    }
}

Upvotes: 4

Related Questions