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 } } } }
关键逻辑
- 用
isSubMenuExpanded状态变量跟踪子菜单的展开/折叠状态 - 父菜单label根据状态动态切换:折叠时显示带图标的
Label,展开时只显示纯文本 - 通过
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
相关产品推荐
相关产品推荐

