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

SwiftUI中能否根据子菜单状态控制父菜单图标显隐?

解决方案:动态控制父菜单图标显示

在SwiftUI中,可以通过状态变量跟踪子菜单的展开状态,动态切换父菜单的label内容,实现需求:

代码实现

struct IconStyleMenuDemo: View {
    // 跟踪子菜单展开状态
    @State private var isSubMenuExpanded = false
    // 当前选中的样式图标
    @State private var selectedIcon = "checkmark.square.fill"
    
    var body: some View {
        Menu {
            Button("Sort Order", action: {})
            Button("Check All", action: {})
            Button("Uncheck All", action: {})
            
            // Icon Style 子菜单
            Menu {
                Button(action: selectStyleA) {
                    Label("Style A", systemImage: "checkmark.square.fill")
                }
                Button(action: selectStyleB) {
                    Label("Style B", systemImage: "square.fill")
                }
                // 更多样式选项...
            } label: {
                // 根据展开状态动态切换label
                if isSubMenuExpanded {
                    Text("Icon Style")
                } else {
                    Label("Icon Style", systemImage: selectedIcon)
                }
            }
            .onTapGesture {
                isSubMenuExpanded.toggle()
            }
            // 点击菜单外部时重置状态
            .onChange(of: isSubMenuExpanded) { isExpanded in
                guard isExpanded else { return }
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                    addGlobalTapGesture()
                }
            }
            
            Button("Save as PDF", action: {})
        } label: {
            Text("Main Menu")
        }
    }
    
    private func selectStyleA() {
        selectedIcon = "checkmark.square.fill"
        isSubMenuExpanded = false
    }
    
    private func selectStyleB() {
        selectedIcon = "square.fill"
        isSubMenuExpanded = false
    }
    
    private func addGlobalTapGesture() {
        guard let window = UIApplication.shared.connectedScenes
                .compactMap({ $0 as? UIWindowScene })
                .first?.windows.first else { return }
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(resetMenuState))
        tapGesture.cancelsTouchesInView = false
        window.addGestureRecognizer(tapGesture)
    }
    
    @objc private func resetMenuState() {
        isSubMenuExpanded = false
        if let window = UIApplication.shared.connectedScenes
                .compactMap({ $0 as? UIWindowScene })
                .first?.windows.first {
            window.gestureRecognizers?.removeAll { $0 is UITapGestureRecognizer }
        }
    }
}

关键逻辑

  1. 用isSubMenuExpanded状态变量跟踪子菜单的展开/折叠状态
  2. 父菜单label根据状态动态切换:折叠时显示带图标的Label,展开时只显示纯文本
  3. 通过onTapGesture切换状态,添加全局点击手势确保用户点击菜单外部时状态同步重置

额外问题:菜单标题下添加子文本

不需要嵌入UIKit,直接用SwiftUI自定义Label即可实现类似Reminders的子文本效果:

代码实现

// 自定义带副标题的Label
struct SubtitleLabel: View {
    let title: String
    let subtitle: String
    
    var body: some View {
        VStack(alignment: .leading, spacing: 3) {
            Text(title)
                .font(.system(size: 16, weight: .medium))
            Text(subtitle)
                .font(.system(size: 13))
                .foregroundColor(.secondary)
        }
        .padding(.vertical, 4)
    }
}

// 使用示例
Menu {
    Button("Due Date", action: {})
    Button("Priority", action: {})
    Button("Title", action: {})
} label: {
    SubtitleLabel(title: "Sort By", subtitle: "Due Date")
}

这种方案轻量且完全基于SwiftUI,调整字体、间距和颜色即可贴近系统样式。


内容的提问来源于stack exchange,提问作者Mark A. Donohoe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 15:45:50