SwiftUI程序化关闭Menu及Menu展开时显示Alert方案咨询
预期效果
接收服务器端消息时,视图弹出Alert弹窗。
问题描述
视图包含TextField和多个Menu组件,当Menu处于展开状态时,收到服务器消息后Alert无法正常显示;只有Menu折叠时,Alert才能正常弹出。
示例代码
struct MenuView: View { @State private var name : String = "" @State private var count : Int = 0 @State private var toggle : Bool = false var body: some View { VStack { TextField("", text: $name) .alert(isPresented: $toggle) { Alert(title: Text("title"), message: Text("message"), dismissButton: .default(Text("OK"))) } Menu { Text("1") Text("2") Text("3") } label: { Text("Top menu1") } Spacer() } .onAppear { // 模拟服务器端延迟消息,需在到期前展开Menu DispatchQueue.main.asyncAfter(deadline: .now() + 5) { print("toggle is true.") toggle = true } } } }
解决方案
1. 程序化折叠展开状态的Menu(iOS 15+)
SwiftUI在iOS 15及以上版本给Menu新增了isPresented绑定参数,可通过自定义状态直接控制Menu的展开与折叠:
修改代码如下:
struct MenuView: View { @State private var name : String = "" @State private var count : Int = 0 @State private var toggle : Bool = false @State private var isMenuExpanded = false // 新增状态变量控制Menu var body: some View { VStack { TextField("", text: $name) .alert(isPresented: $toggle) { Alert(title: Text("title"), message: Text("message"), dismissButton: .default(Text("OK"))) } // 绑定isPresented到自定义状态 Menu(isPresented: $isMenuExpanded) { Text("1") Text("2") Text("3") } label: { Text("Top menu1") } Spacer() } .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 5) { print("toggle is true.") // 先关闭Menu,再触发Alert isMenuExpanded = false toggle = true } } } }
触发Alert前,先将isMenuExpanded设为false强制折叠Menu,之后Alert即可正常弹出。
2. 兼容低版本:用UIKit弹窗绕过SwiftUI层级限制
如果需要兼容iOS 14及以下版本,无法使用isPresented参数,可以直接调用UIKit的UIAlertController。它的弹窗层级更高,能覆盖展开的Menu:
struct MenuView: View { @State private var name : String = "" @State private var count : Int = 0 var body: some View { VStack { TextField("", text: $name) Menu { Text("1") Text("2") Text("3") } label: { Text("Top menu1") } Spacer() } .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 5) { showAlert() } } } private func showAlert() { // 获取当前顶层ViewController guard let topVC = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .compactMap({$0 as? UIWindowScene}) .first?.windows .filter({$0.isKeyWindow}) .first?.rootViewController else { return } let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) topVC.present(alert, animated: true) } }
这种方式不管Menu是否展开,弹窗都会正常显示在最上层。
内容的提问来源于stack exchange,提问作者Alpha
相关产品推荐
相关产品推荐

