SwiftUI macOS应用:实现窗口层级交互控制及焦点恢复方案问询
在SwiftUI macOS应用中实现层级式模态窗口(仅当前窗口可交互)
需求说明
在基于SwiftUI的macOS应用里,要实现以下效果:
- 弹出新窗口时,之前的所有窗口(包括主窗口)都变成不可交互状态,只有最新弹出的子窗口保持活跃
- 窗口层级严格遵循:主窗口 > 子窗口 > 子子窗口 > 更深层级子窗口,只有当前活跃的窗口能被操作
- 关闭子窗口时,打开它的上一级窗口自动恢复控制权;所有子窗口都关闭后,主窗口恢复可交互状态
修改后的完整代码
import SwiftUI struct ContentView: View { @State private var mainWindow: NSWindow? var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") Button("Open new Window") { guard let parentWindow = mainWindow else { return } let _ = ChildView(string: "ChildView") .openInWindow(title: "ChildView", sender: self, parentWindow: parentWindow) } } .padding() .onAppear { // 获取主窗口引用 mainWindow = NSApplication.shared.windows.first { $0.isMainWindow } } } } extension View { @discardableResult func openInWindow(title: String, sender: Any?, parentWindow: NSWindow) -> NSWindow { let controller = NSHostingController(rootView: self) let window = NSWindow(contentViewController: controller) // 设置窗口层级:子窗口层级比父窗口高一级 window.level = parentWindow.level + 1 // 窗口关闭时不自动释放,确保能处理后续逻辑 window.isReleasedWhenClosed = false // 禁用父窗口交互 parentWindow.isEnabled = false window.center() window.title = title window.makeKeyAndOrderFront(sender) // 设置窗口代理,处理关闭时的父窗口恢复逻辑 window.delegate = WindowDelegate(parentWindow: parentWindow) return window } } // 窗口代理类,处理子窗口关闭时恢复父窗口状态 class WindowDelegate: NSObject, NSWindowDelegate { private weak var parentWindow: NSWindow? init(parentWindow: NSWindow) { self.parentWindow = parentWindow super.init() } func windowWillClose(_ notification: Notification) { guard let parentWindow = parentWindow else { return } // 恢复父窗口的交互性 parentWindow.isEnabled = true // 激活父窗口,让它重新获得控制权 parentWindow.makeKeyAndOrderFront(nil) } } struct ChildView: View { let string: String @State private var currentWindow: NSWindow? var body: some View { VStack { Text(string).bold() HStack { Button("Open new Window") { guard let parentWindow = currentWindow else { return } let _ = ChildView(string: "Another " + string) .openInWindow(title: "Another " + string, sender: self, parentWindow: parentWindow) } Button("Dismiss") { currentWindow?.close() } .buttonStyle(.borderedProminent) } } .frame(width: 400.0, height: 200.0) .onAppear { // 获取当前子窗口的引用 currentWindow = NSApplication.shared.keyWindow } } }
关键改动说明
- 窗口父子关联:给
openInWindow扩展新增parentWindow参数,明确每个子窗口的父窗口,关闭时能精准定位上一级窗口 - 层级控制:通过
window.level = parentWindow.level + 1确保子窗口层级始终高于父窗口,形成主窗口→子窗口→子子窗口的层级链 - 交互状态切换:打开子窗口时禁用父窗口的交互;子窗口关闭时,通过代理方法
windowWillClose恢复父窗口的交互性并激活它 - 窗口生命周期管理:设置
window.isReleasedWhenClosed = false,避免窗口被自动释放,确保代理事件能正常触发 - 窗口引用获取:在视图
onAppear时获取当前窗口的引用,避免依赖NSApplication.shared.keyWindow可能出现的错误
内容的提问来源于stack exchange,提问作者Mango
相关产品推荐
相关产品推荐

