MacOS SwiftUI如何阻止窗口关闭?实现窗口隐藏而非销毁
Got it, let's fix this issue for you! The problem here is that when you click the red close button on an NSWindow, its default behavior is to destroy the window entirely (deallocate it), which is why you're hitting a segmentation fault when trying to show it again—your configurationWindow reference is pointing to deallocated memory.
Here's how to make the close button just hide the window instead of destroying it, using two straightforward approaches:
Approach 1: Use NSWindowDelegate (Recommended)
This method keeps things flexible without needing to subclass NSWindow.
First, make sure your class (whether it's your AppDelegate, a status bar manager, etc.) conforms to the NSWindowDelegate protocol. Then:
Set the window's delegate when creating it
Update your window initialization code to assign the delegate:let configurationView = ConfigurationView().environmentObject(store) configurationWindow = NSWindow( contentRect: NSRect(x:0, y: 0, width: 480, height: 500), styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView], backing: .buffered, defer: false ) configurationWindow?.delegate = self // Add this line configurationWindow?.center() configurationWindow?.setFrameAutosaveName("BSchauer") let hostingController = NSHostingController(rootView: configurationView) configurationWindow?.contentViewController = hostingController configurationWindow?.makeKeyAndOrderFront(nil) configurationWindow?.setIsVisible(false)Implement the
windowShouldClosedelegate method
Add this extension to your class to intercept the close request, hide the window, and prevent it from being destroyed:extension YourClassName: NSWindowDelegate { func windowShouldClose(_ sender: NSWindow) -> Bool { // Hide the window instead of closing it sender.setIsVisible(false) // Sync with your popover logic (match what you do in toggleConfigurationWindow) if let button = statusBarItem.button { popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) } // Return false to stop the window from being destroyed return false } }
Approach 2: Subclass NSWindow
If you prefer encapsulating the behavior directly in the window itself, subclass NSWindow and override the close action:
Create a custom NSWindow subclass
class HideableWindow: NSWindow { override func performClose(_ sender: Any?) { // Override the default close behavior to just hide self.setIsVisible(false) // Optional: Sync popover state (adjust the reference to your app's logic as needed) if let appDelegate = NSApp.delegate as? YourAppDelegate { if let button = appDelegate.statusBarItem.button { appDelegate.popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) } } } }Use your custom window class when initializing
configurationWindow = HideableWindow( contentRect: NSRect(x:0, y: 0, width: 480, height: 500), styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView], backing: .buffered, defer: false )
Why Your Previous Attempt Didn't Work
You mentioned trying to override windowShouldClose in an NSViewController—this method belongs to the NSWindowDelegate protocol, not NSViewController. That's why it wasn't intercepting the close request. By assigning the window's delegate and implementing the method there, you'll correctly catch the close action.
Also, double-check that your configurationWindow property is a strong reference (e.g., var configurationWindow: NSWindow? instead of weak) so it doesn't get deallocated unexpectedly.
内容的提问来源于stack exchange,提问作者Oscar Franco

