You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:

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:

  1. 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)
    
  2. Implement the windowShouldClose delegate 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:

  1. 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)
                }
            }
        }
    }
    
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 11:57:37