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

SwiftUI中如何修改默认Menu的尺寸及显示位置?

解决方案

直接用SwiftUI原生的Menu组件没法实现你要的两个效果(它的弹出位置和宽度是系统默认控制的),换用自定义下拉菜单就能搞定,具体实现如下:

核心思路

  • 用VStack把输入框和下拉列表绑定在一起,通过showDropdown状态控制列表的显示/隐藏
  • 让下拉列表和输入框继承同一个父容器的宽度,保证两者宽度一致
  • 增加点击输入框切换状态、点击选项选中后收起、点击外部区域收起的交互逻辑

完整代码

import SwiftUI

struct DropdownTextField: View {
    @State private var showDropdown = false
    @Binding var selectedOption: String
    
    let label: String
    let options: [String]
    let optionToLabel: (String) -> String
    
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(label)
                .font(.caption)
                .foregroundColor(.secondary)
            
            // 模拟输入框的按钮区域
            Button(action: {
                withAnimation(.easeInOut) {
                    showDropdown.toggle()
                }
            }) {
                HStack {
                    Text(optionToLabel(selectedOption))
                        .foregroundColor(.primary)
                    Spacer()
                    Image(systemName: showDropdown ? "chevron.up" : "chevron.down")
                        .foregroundColor(.secondary)
                }
                .padding(12)
                .background(Color(.systemGray6))
                .cornerRadius(8)
            }
            
            // 自定义下拉列表
            if showDropdown {
                VStack(spacing: 0) {
                    ForEach(options, id: \.self) { option in
                        Button(action: {
                            selectedOption = option
                            withAnimation(.easeInOut) {
                                showDropdown = false
                            }
                        }) {
                            Text(optionToLabel(option))
                                .padding(12)
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                        .background(Color(.systemBackground))
                        
                        // 选项间添加分割线
                        if option != options.last {
                            Divider()
                        }
                    }
                }
                .background(Color(.systemGray6))
                .cornerRadius(8)
                .shadow(color: .black.opacity(0.1), radius: 4, x: 0, y: 2)
                .transition(.opacity.combined(with: .move(edge: .top)))
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        // 点击外部区域收起下拉
        .onTapGesture {
            guard showDropdown else { return }
            withAnimation(.easeInOut) {
                showDropdown = false
            }
        }
    }
}

#Preview {
    let options = ["Name 1", "Name 2", "Name 3"]
    
    @State var selectedOption = options[0]
    
    return VStack(spacing: 24) {
        DropdownTextField(selectedOption: $selectedOption, label: "Select an option", options: options, optionToLabel: {$0})
        DropdownTextField(selectedOption: $selectedOption, label: "Select an option", options: options, optionToLabel: {$0})
    }
    .padding()
}

效果说明

  1. 下拉位置:下拉列表直接位于输入框正下方,不会覆盖上方的组件
  2. 宽度一致:输入框和下拉列表都使用maxWidth: .infinity,自动匹配父容器宽度,保持两者完全一致
  3. 交互体验:添加了平滑的展开/收起动画,点击选项自动选中并收起,点击组件外部也能关闭下拉

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 01:05:11