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

SwiftUI & macOS:如何检测最后一个窗口关闭并弹出应用退出确认提示

问题根因

你当前的逻辑存在时序问题:当applicationShouldTerminateAfterLastWindowClosed返回true时,系统会先销毁最后一个窗口,再触发应用退出的回调applicationShouldTerminate,这时候承载alert的ContentView已经随着窗口销毁被卸载了,自然不会弹出任何提示。

推荐你直接在AppDelegate层面处理窗口关闭拦截和弹窗,不需要在SwiftUI视图层绑定alert,逻辑更简单也不会出现时序问题,修改方案如下:


修改后的Application类代码

class Application: NSObject, NSApplicationDelegate, ObservableObject {
    override init() {
        super.init()
        // 全局监听所有窗口的关闭事件
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(windowWillClose(_:)),
            name: NSWindow.willCloseNotification,
            object: nil
        )
    }
    
    // 关闭系统默认的最后一个窗口退出逻辑,改为自行控制
    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        return false
    }
    
    @objc private func windowWillClose(_ notification: Notification) {
        guard let window = notification.object as? NSWindow else { return }
        // 统计当前所有可见的正常主窗口,过滤辅助悬浮窗、弹窗等非主窗口
        let visibleNormalWindows = NSApplication.shared.windows.filter {
            $0.isVisible && $0.styleMask.contains(.titled)
        }
        // 判断当前要关闭的是不是最后一个正常可见窗口
        if visibleNormalWindows.count == 1 {
            // 先拦截本次窗口关闭事件
            notification.object = nil
            DispatchQueue.main.async {
                // 直接用AppKit原生弹窗,不受视图生命周期影响
                let alert = NSAlert()
                alert.messageText = "确定要退出应用吗?"
                alert.informativeText = "关闭最后一个窗口将会退出应用,是否继续操作?"
                alert.addButton(withTitle: "取消")
                alert.addButton(withTitle: "退出")
                alert.alertStyle = .warning
                let response = alert.runModal()
                if response == .alertSecondButtonReturn {
                    // 用户确认退出,先关闭窗口再终止应用
                    window.close()
                    NSApplication.shared.terminate(nil)
                }
            }
        }
    }
}

应用入口绑定Delegate

在你的App主结构中,通过NSApplicationDelegateAdaptor绑定上面的Application类即可生效:

@main
struct MyApp: App {
    @NSApplicationDelegateAdaptor(Application.self) private var appDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

原来ContentView中的alert逻辑、WindowAccessor工具类都可以直接删除,不需要再使用。

内容的提问来源于stack exchange,提问作者Jav Solo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 06:06:04