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

SwiftUI中如何为菜单添加副标题并实现选中项灰色显示?

实现苹果Stocks风格的选中项灰色菜单效果

SwiftUI原生的Picker(.menu)确实没有提供菜单展开时选中项灰色显示的默认样式,要实现这个效果,我们可以借助UIKit的UIMenu封装自定义组件,UIKit菜单支持更灵活的选中状态样式定制。

自定义菜单组件实现

import SwiftUI

struct CustomMenuPicker: UIViewRepresentable {
    let systemImage: String
    let options: [(title: String, tag: Int)]
    @Binding var selectedTag: Int
    
    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        // 设置初始显示的标题和图标
        if let selectedOption = options.first(where: { $0.tag == selectedTag }) {
            button.setTitle(selectedOption.title, for: .normal)
        }
        button.setImage(UIImage(systemName: systemImage), for: .normal)
        button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)
        button.semanticContentAttribute = .forceRightToLeft // 让图标在文字左侧,匹配SwiftUI默认样式
        
        // 构建菜单选项
        let actions = options.map { option in
            let action = UIAction(title: option.title, state: option.tag == selectedTag ? .on : .off) { _ in
                selectedTag = option.tag
                button.setTitle(option.title, for: .normal)
            }
            
            // 给选中项设置灰色文字
            if option.tag == selectedTag {
                let attributedTitle = NSAttributedString(string: option.title, attributes: [
                    .foregroundColor: UIColor.systemGray
                ])
                action.attributedTitle = attributedTitle
            }
            
            return action
        }
        
        let menu = UIMenu(title: "", children: actions)
        button.menu = menu
        button.showsMenuAsPrimaryAction = true
        
        return button
    }
    
    func updateUIView(_ uiView: UIButton, context: Context) {
        // 选中状态变化时更新菜单样式
        guard let menu = uiView.menu else { return }
        let updatedActions = options.map { option in
            let action = UIAction(title: option.title, state: option.tag == selectedTag ? .on : .off) { _ in
                selectedTag = option.tag
                uiView.setTitle(option.title, for: .normal)
            }
            
            if option.tag == selectedTag {
                let attributedTitle = NSAttributedString(string: option.title, attributes: [
                    .foregroundColor: UIColor.systemGray
                ])
                action.attributedTitle = attributedTitle
            } else {
                action.attributedTitle = NSAttributedString(string: option.title)
            }
            
            return action
        }
        
        uiView.menu = UIMenu(title: "", children: updatedActions)
        // 更新按钮当前显示的标题
        if let selectedOption = options.first(where: { $0.tag == selectedTag }) {
            uiView.setTitle(selectedOption.title, for: .normal)
        }
    }
}

在SwiftUI视图中使用

struct ContentView: View {
    @State private var selectedOption = 0
    
    var body: some View {
        CustomMenuPicker(
            systemImage: "chart.line.uptrend.xyaxis",
            options: [
                (title: "Prijswijziging", tag: 0),
                (title: "Procentuele wijziging", tag: 1),
                (title: "Marktkapitalisatie", tag: 2)
            ],
            selectedTag: $selectedOption
        )
        .padding()
    }
}

关键说明

  • 通过UIButton结合UIMenu替代原生Picker,实现完全自定义的菜单样式
  • 利用UIAction的state标记选中状态,同时通过attributedTitle给选中项设置灰色文字,匹配Stocks应用的效果
  • updateUIView方法会在绑定的选中值变化时同步更新菜单的选中状态和文字样式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 12:35:17