Mac状态栏应用设置视图启动及关闭后重启问题咨询
Hey there! Let's work through getting your settings view up and running properly, and tackle that restart-after-close functionality too. The NSWorkspace.shared().launchApplication("AppName") approach isn't right here because it tries to spin up a whole new instance of your app—instead, we need to show the settings view directly from your running status bar process.
Step 1: Show the Settings View Properly
First, your settings view should be wrapped in an NSWindowController (or NSViewController if you're using storyboards). Here's how to trigger it from your NSMenuItem:
Create a window controller for your settings (let's say you have a XIB named
SettingsWindow):import Cocoa class SettingsWindowController: NSWindowController { override var windowNibName: NSNib.Name? { return NSNib.Name("SettingsWindow") } }In your app's main logic (like
AppDelegateor the menu handler), add a property to hold the window controller (this prevents it from being deallocated when the window closes):private var settingsWindowController: SettingsWindowController?Add the action method for your
NSMenuItem:@IBAction func showSettings(_ sender: NSMenuItem) { // Create the controller if it doesn't exist yet if settingsWindowController == nil { settingsWindowController = SettingsWindowController() } // Show the window and bring it to the front settingsWindowController?.showWindow(self) settingsWindowController?.window?.makeKeyAndOrderFront(nil) // If your app is a LSUIElement (no Dock icon), activate it to ensure the window responds to input NSApp.activate(ignoringOtherApps: true) }
Step 2: Handle Restarting the View After Close
There are two scenarios here—let's cover both:
Scenario 1: Reopen the settings view when the user clicks the menu again after closing it
The code above already handles this! Since we're holding onto the settingsWindowController reference, closing the window just hides it. The next time the user clicks the menu, showWindow() will bring it back. If you want to completely reset the view each time (instead of reusing it), just set settingsWindowController = nil when the window closes:
// Add this to your SettingsWindowController override func windowWillClose(_ notification: Notification) { // Tell the parent to release the controller if let appDelegate = NSApp.delegate as? AppDelegate { appDelegate.settingsWindowController = nil } }
Scenario 2: Auto-open the settings view when your app restarts (after being closed)
To do this, use UserDefaults to store a flag that tells the app to open settings on launch:
In your
AppDelegate, check the flag when the app finishes launching:func applicationDidFinishLaunching(_ aNotification: Notification) { let defaults = UserDefaults.standard if defaults.bool(forKey: "ShouldOpenSettingsOnLaunch") { showSettings(NSMenuItem()) // Trigger the settings view defaults.set(false, forKey: "ShouldOpenSettingsOnLaunch") // Reset the flag } }Set the flag when you want the app to open settings on next restart (e.g., when the user closes the settings window, or when they exit the app):
// Example: Set the flag when the settings window closes override func windowWillClose(_ notification: Notification) { UserDefaults.standard.set(true, forKey: "ShouldOpenSettingsOnLaunch") }
Key Notes to Avoid Common Pitfalls
- Don't use
NSWorkspaceto launch your own app: This creates a duplicate instance, which is almost never what you want for a status bar app. - LSUIElement considerations: If your app has
LSUIElement = YESinInfo.plist(no Dock icon), make sure to callNSApp.activate(ignoringOtherApps: true)when showing the settings window—otherwise, the window might not receive user input. - Single instance guarantee: Ensure your app only runs one instance. You can enforce this by adding a check in
applicationDidFinishLaunchingor settingLSBackgroundOnly = YES(though the latter has other implications).
内容的提问来源于stack exchange,提问作者Antoine

