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

SwiftUI如何检测contextMenu是否打开 可通过@State变量实现吗?

你之前的两种写法不生效,是因为contextMenu modifier的闭包是菜单内容构建闭包,仅在系统需要渲染菜单选项时才会调用,闭包内仅可返回菜单视图,直接执行打印逻辑、或给EmptyView加onAppear都不会触发回调。

检测contextMenu打开状态的实现方案

方案1:使用系统内置Environment变量(iOS 15+ 推荐)

SwiftUI 提供了专用的环境变量来检测当前视图上下文菜单的激活状态,直接监听即可实现需求:

struct PostGridView: View {
    // 读取当前视图的contextMenu激活状态
    @Environment(\.isContextMenuInteractionActive) private var isContextMenuActive
    // 控制视图样式的状态变量
    @State private var postCornerRadius: CGFloat = 8
    @State private var showPostTitle = false

    var body: some View {
        VStack {
            if showPostTitle {
                Text("帖子标题")
                    .font(.caption)
            }
            Image("post-cover")
                .resizable()
                .scaledToFill()
                .frame(width: 150, height: 150)
                .cornerRadius(postCornerRadius)
        }
        .contextMenu {
            // 菜单选项内容
            Button("分享") {}
            Button("收藏") {}
            Button("举报", role: .destructive) {}
        }
        // 监听状态变化执行逻辑
        .onChange(of: isContextMenuActive) { isActive in
            if isActive {
                print("contextMenu 已打开")
                // 修改视图样式
                postCornerRadius = 16
                showPostTitle = true
            } else {
                print("contextMenu 已关闭")
                // 恢复默认样式
                postCornerRadius = 8
                showPostTitle = false
            }
        }
    }
}

注意:该环境变量仅在绑定了contextMenu的视图及其子视图层级内可以读取到正确值,不会受页面其他contextMenu的影响

方案2:低版本iOS兼容方案(iOS 13+)

如果需要支持iOS 15以下的系统,可以通过监听UIKit的全局通知实现:

// 扩展通知名称
extension Notification.Name {
    static let menuWillShow = Notification.Name("UIMenuControllerWillShowMenuNotification")
    static let menuWillHide = Notification.Name("UIMenuControllerWillHideMenuNotification")
}

struct CompatPostGridView: View {
    @State private var isContextMenuActive = false

    var body: some View {
        Image("post-cover")
            .resizable()
            .scaledToFill()
            .frame(width: 150, height: 150)
            .contextMenu {
                Button("分享") {}
            }
            .onAppear {
                // 监听菜单弹出/收起通知
                NotificationCenter.default.addObserver(forName: .menuWillShow, object: nil, queue: .main) { _ in
                    isContextMenuActive = true
                    print("contextMenu 已打开")
                }
                NotificationCenter.default.addObserver(forName: .menuWillHide, object: nil, queue: .main) { _ in
                    isContextMenuActive = false
                    print("contextMenu 已关闭")
                }
            }
    }
}

注意:该方案是全局监听,同一页面存在多个contextMenu的场景需要额外判断当前触发的视图归属


Instagram交互效果说明

你提到的Instagram帖子长按效果就是基于上述状态监听实现的:

  • 网格默认状态下,帖子裁剪为正方形、标题元素默认隐藏
  • 检测到contextMenu激活时,修改视图裁剪规则、显示隐藏的标题,配合系统自带的缩放过渡动画即可实现对应的效果,无需自定义长按手势模拟contextMenu。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 13:45:04