如何通过菜单栏按钮重新打开已关闭/最小化的macOS应用窗口
macOS 点击状态栏按钮重新唤起已关闭/最小化主窗口实现
问题原因
原有代码无法生效的核心原因有5点:
- 未强引用主窗口控制器(
MainWindowController),窗口关闭后实例被系统自动回收,NSApp.windows中无法再获取到对应窗口实例 - 直接通过
NSApp.windows.last获取主窗口不可靠,出现其他窗口时会取错对象,窗口被回收后也取不到值 - 未配置窗口关闭时不释放,
NSWindow默认isReleasedWhenClosed属性为true,关闭后窗口实例会被直接销毁 - 未实现应用终止策略,部分macOS版本下关闭最后一个窗口会直接退出应用,导致状态栏按钮一并消失
- 未处理窗口最小化场景,最小化状态下仅调用
makeKeyAndOrderFront无法自动恢复窗口显示
修改步骤
1. 配置Storyboard
打开Main.storyboard,选中主窗口对应的MainWindowController,在右侧Identity检查器面板中:
- 将
Storyboard ID设置为MainWindowController - 勾选
Use Storyboard ID选项
2. 修改AppDelegate代码
在AppDelegate中新增对主窗口控制器的强引用,实现关窗不退出逻辑,修正状态栏按钮的唤起方法,完整代码如下:
class AppDelegate: NSObject, NSApplicationDelegate { private var statusItem: NSStatusItem! // 强引用主窗口控制器,防止实例被回收 private var mainWindowController: MainWindowController? func applicationDidFinishLaunching(_ aNotification: Notification) { // 初始化状态栏按钮 statusItem = NSStatusBar.system.statusItem(withLength: 16.0) if let button = statusItem.button { button.image = NSImage(named: "remote-control") button.image?.size = NSSize(width: 16.0, height: 16.0) button.image?.isTemplate = true button.action = #selector(bringToFront(sender:)) } // 从Storyboard加载主窗口控制器 let storyboard = NSStoryboard(name: "Main", bundle: nil) guard let mainWC = storyboard.instantiateController(withIdentifier: "MainWindowController") as? MainWindowController else { return } self.mainWindowController = mainWC mainWC.showWindow(nil) } // 关闭最后一个窗口时不终止应用 func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return false } func applicationWillTerminate(_ aNotification: Notification) { print("terminate") } func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } @objc func bringToFront(sender: AnyObject?) { NSApplication.shared.activate(ignoringOtherApps: true) guard let mainWC = mainWindowController, let window = mainWC.window else { return } // 最小化状态下先恢复窗口 if window.isMiniaturized { window.deminiaturize(nil) } // 将窗口前置并设为关键响应窗口 window.makeKeyAndOrderFront(nil) } }
3. 修改MainWindowController代码
在windowDidLoad中配置窗口关闭时不释放实例,完整代码如下:
class MainWindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() window?.title = "" let styleMask: NSWindow.StyleMask = [.closable, .titled, .miniaturizable] window?.styleMask = styleMask // 关闭窗口时不释放实例,保证后续可以重新唤起 window?.isReleasedWhenClosed = false } }
验证效果
修改完成后运行应用:
- 点击窗口关闭按钮,窗口消失,应用不退出,状态栏按钮保留
- 点击窗口最小化按钮,窗口收进Dock
- 两种场景下点击状态栏按钮,都可以正常将主窗口恢复并前置到屏幕最前方
内容的提问来源于stack exchange,提问作者Fake Panda
相关产品推荐
相关产品推荐

