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

SwiftUI中Menu未关闭时点击Button无法弹出sheet的问题及解决需求

SwiftUI Menu展开时快速点击Sheet触发按钮失效问题及解决方案

问题详情

开发SwiftUI视图时遇到异常:视图包含Menu组件和触发Sheet弹窗的Button,当Menu处于展开状态时快速点击按钮,Sheet无法弹出,且后续点击该按钮也完全失效。控制台日志报错:

Attempt to present <TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView: 0x113811a00> on <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x112825600> (from <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x112825600>) which is already presenting <_UIContextMenuActionsOnlyViewController: 0x10c1183c0>.

仅快速点击会触发该问题,长按按钮则正常。测试环境为Xcode 15,问题覆盖iOS 15、16、17版本。

复现代码:

import SwiftUI

@main
struct menuAndButtonApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State var modalIsPresented = false

    var body: some View {
        HStack(spacing: 28) {
            Menu("Open Menu") {
                Text("Menu Item")
            }
            Button("Present Modal") {
                modalIsPresented = true
            }
        }.sheet(isPresented: $modalIsPresented) {
            Text("Here is a sheet")
        }
    }
}

目前已验证用DispatchQueue.main.asyncAfter(.now() + 0.2) { }包裹按钮操作可解决,但该方案需无条件延迟,无法精准判断Menu是否展开。


可行Workaround方案

方案1:基于UIKit控制器状态检测

通过UIViewControllerRepresentable获取底层宿主控制器,仅当无正在展示的控制器时触发Sheet,避免冲突:

import SwiftUI

struct PresentationChecker: UIViewControllerRepresentable {
    @Binding var isTriggering: Bool
    var onSafePresent: () -> Void
    
    func makeUIViewController(context: Context) -> UIViewController {
        let vc = UIViewController()
        vc.view.backgroundColor = .clear
        return vc
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        guard isTriggering else { return }
        // 检查当前是否有正在弹出的控制器
        if uiViewController.presentingViewController == nil {
            onSafePresent()
        } else {
            isTriggering = false
        }
    }
}

struct ContentView: View {
    @State var modalIsPresented = false
    @State var triggerModal = false

    var body: some View {
        HStack(spacing: 28) {
            Menu("Open Menu") {
                Text("Menu Item")
            }
            Button("Present Modal") {
                triggerModal = true
            }
        }
        .overlay(PresentationChecker(isTriggering: $triggerModal) {
            modalIsPresented = true
        })
        .sheet(isPresented: $modalIsPresented) {
            Text("Here is a sheet")
        }
    }
}

方案2:追踪Menu展开状态

通过监听Menu的点击和消失事件,判断是否需要延迟触发Sheet:

struct ContentView: View {
    @State var modalIsPresented = false
    @State var isMenuExpanded = false

    var body: some View {
        HStack(spacing: 28) {
            Menu("Open Menu") {
                Text("Menu Item")
                    .onDisappear {
                        isMenuExpanded = false
                    }
            }
            .onTapGesture {
                isMenuExpanded = true
            }
            
            Button("Present Modal") {
                if isMenuExpanded {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
                        modalIsPresented = true
                    }
                } else {
                    modalIsPresented = true
                }
            }
        }
        .sheet(isPresented: $modalIsPresented) {
            Text("Here is a sheet")
        }
    }
}

方案3:临时替换为Alert(应急用)

若上述方案仍有问题,可临时用Alert替代Sheet,规避SwiftUI的Sheet与Menu的冲突:

struct ContentView: View {
    @State var showAlert = false

    var body: some View {
        HStack(spacing: 28) {
            Menu("Open Menu") {
                Text("Menu Item")
            }
            Button("Present Modal") {
                showAlert = true
            }
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("Here is a sheet"), dismissButton: .default(Text("OK")))
        }
    }
}

内容的提问来源于stack exchange,提问作者Andrew Bennet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:42:50