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

SwiftUI程序化关闭Menu及Menu展开时显示Alert方案咨询

Swift 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 19:42:10