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

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
  }
}

我的问题

  1. 能否隐藏新创建窗口右上角的红色关闭按钮?
  2. 如何获取该窗口对应的NSWindow实例?
  3. 用户尝试点击红色关闭按钮时,如何执行特定代码并在某些情况下阻止窗口关闭?

解答

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 01:11:08