Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 706
Wendell Avatar asked Sep 02 '25 05:09

Wendell


1 Answers

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()
        }
    }
}
like image 113
Ely Avatar answered Sep 04 '25 22:09

Ely