3
votes

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!

1
@Willeke You didn't read the last part of the questionEly
I did read your question. What makes you think I didn't?Willeke
@Willeke This question is about deactivating and activating the app from a NSStatusItem in the menubar, including hiding / unhiding the dock icon, while your suggested duplicate is about a standard close and open window scenario.Ely
The question is "How to Make macOS App Window Hidden When Closed and Reopened With Menu Bar?". It's about a window.Willeke

1 Answers

3
votes

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()
        }
    }
}