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
相关产品推荐
相关产品推荐

