SwiftUI可关闭Popover问题:点击外部或浏览器后未自动消失
问题根源
你的代码存在几个关键问题导致Popover无法自动关闭:
- Popover绑定的自定义
Binding的set闭包为空,系统无法在点击外部时更新状态 togglePopover函数的参数名与State变量selector重名,实际并未修改到State值- 打开URL后未重置控制Popover显示的
selector变量 - 未处理MenuBarExtra窗口失去焦点的场景(比如切换到浏览器)
修正方案
以下是完整的修复代码,附带关键修改说明:
struct AppMenu: View { @State private var selector: String = "" // 监听应用活跃状态 @Environment(\.scenePhase) private var scenePhase // 修正参数名,避免与State变量冲突 private func togglePopover(_ itemId: String) { // 点击同一行关闭Popover,否则切换到对应行 selector = selector == itemId ? "" : itemId } private func openURLInSafari(_ urlString: String) { guard let url = URL(string: urlString) else { return } NSWorkspace.shared.open(url) } var body: some View { VStack { List { ForEach(itemList, id: \.self) { item in Button(action: { togglePopover(item.Id) }) { Label(item.Content, systemImage: "arrow.right.circle.fill") } // 完善Popover的Binding,处理系统触发的关闭事件 .popover(isPresented: Binding( get: { selector == item.Id }, set: { isPresented in if !isPresented { selector = "" } } )) { VStack { Button("Open in sharari") { openURLInSafari(item.Content) // 打开URL后手动关闭Popover selector = "" } .padding() } .frame(width: 200, height: 100) } } } } // 应用失去活跃状态时,自动关闭Popover .onChange(of: scenePhase) { newPhase in if newPhase != .active { selector = "" } } } } // 主应用场景 MenuBarExtra("MenuStuff", systemImage: "book.fill") { AppMenu() } .menuBarExtraStyle(.window)
关键修改点说明
- 修正
togglePopover函数:将参数名改为itemId避免与State变量冲突,同时实现点击同一行切换Popover显示/隐藏的逻辑 - 完善Popover的Binding:在
set闭包中监听系统触发的关闭操作,自动重置selector - 打开URL后手动重置状态:点击按钮打开浏览器时,立即清空
selector关闭Popover - 监听应用活跃状态:通过
scenePhase监听应用是否处于活跃状态,切换到其他应用时自动关闭Popover
内容的提问来源于stack exchange,提问作者Jason Bhan
相关产品推荐
相关产品推荐

