SwiftUI中@Environment(\.openWindow)打开窗口的操作与拦截关闭问题
SwiftUI窗口相关问题解答
我的实现情况
我在SwiftUI应用中通过以下代码使用@Environment(\.openWindow)打开新窗口,运行正常:
@Environment(\.openWindow) private var openWindow // ... openWindow(value: item.id)
对应的窗口定义:
WindowGroup("Item", for: Item.ID.self) { $itemId in if let id = itemId { ItemWindow(persistentID: id) } }
我尝试通过AppDelegate实现NSWindowDelegate但代理方法没触发,只有AppDelegate自身的方法正常运行:
@main struct Window_TestApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate }
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { func applicationDidFinishLaunching(_ notification: Notification) { print("Did Finish launching") } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { print("applicationShouldTerminate") //return .terminateCancel return .terminateNow } func windowWillClose(_ notification: Notification) { print("windowWillClose") } func windowShouldClose(_ sender: NSWindow) -> Bool { print("windowShouldClose") return true } }
我的问题
- 能否隐藏新创建窗口右上角的红色关闭按钮?
- 如何获取该窗口对应的
NSWindow实例? - 用户尝试点击红色关闭按钮时,如何执行特定代码并在某些情况下阻止窗口关闭?
解答
1. 隐藏关闭按钮
可以通过获取窗口实例后修改其styleMask来移除关闭按钮。我们可以自定义一个NSViewRepresentable视图来获取窗口,然后修改样式:
struct NSWindowAccessor: NSViewRepresentable { let action: (NSWindow) -> Void func makeNSView(context: Context) -> NSView { let view = NSView() DispatchQueue.main.async { if let window = view.window { action(window) } } return view } func updateNSView(_ nsView: NSView, context: Context) {} }
接着在你的ItemWindow视图中使用这个NSWindowAccessor:
struct ItemWindow: View { let persistentID: Item.ID var body: some View { Text("Item Window \(persistentID)") .background(NSWindowAccessor { window in // 移除关闭按钮样式 window.styleMask.remove(.closable) }) } }
2. 获取NSWindow实例
上面的NSWindowAccessor是最可靠的方式之一:通过NSViewRepresentable嵌入到SwiftUI视图中,利用视图的window属性获取对应的NSWindow实例。
另外,也可以通过NSApplication.shared.windows遍历所有窗口,根据窗口标题或内容视图的标识找到目标窗口,但这种方式不如前者精准。
3. 拦截关闭事件并阻止窗口关闭
之前AppDelegate中的NSWindowDelegate方法没触发,是因为没有将代理绑定到目标窗口上。我们需要创建独立的窗口代理类,在获取到窗口实例后手动设置代理:
首先定义自定义代理类:
class WindowDelegate: NSObject, NSWindowDelegate { // 自定义闭包,用来处理关闭判断逻辑 var shouldCloseHandler: ((NSWindow) -> Bool)? func windowShouldClose(_ sender: NSWindow) -> Bool { // 执行你需要的特定代码 print("用户尝试关闭窗口") // 调用闭包获取是否允许关闭的结果 return shouldCloseHandler?(sender) ?? true } func windowWillClose(_ notification: Notification) { // 窗口即将关闭时的额外操作 print("窗口即将关闭") } }
然后在ItemWindow中使用这个代理,同时要保留代理实例避免被释放:
struct ItemWindow: View { let persistentID: Item.ID // 保存代理实例,防止被ARC释放 private let windowDelegate = WindowDelegate() var body: some View { Text("Item Window \(persistentID)") .background(NSWindowAccessor { window in // 将代理绑定到当前窗口 window.delegate = windowDelegate // 设置关闭拦截逻辑 windowDelegate.shouldCloseHandler = { window in // 这里写你的判断逻辑,比如弹出确认对话框 let alert = NSAlert() alert.messageText = "确定要关闭这个窗口吗?" alert.addButton(withTitle: "确定") alert.addButton(withTitle: "取消") let response = alert.runModal() // 返回true允许关闭,false阻止关闭 return response == .alertFirstButtonReturn } }) } }
这样用户点击关闭按钮时,就会触发windowShouldClose方法,执行你定义的代码,并根据返回值决定是否关闭窗口。
内容的提问来源于stack exchange,提问作者mica
相关产品推荐
相关产品推荐

