SwiftUI Menu下拉菜单弹出位置修改(顶部改底部)咨询
我正在学习Swift、SwiftUI及相关组件,目前在Menu组件的按钮弹出样式上遇到了问题。我的应用顶部栏有一个可触发Sheet的添加按钮,当Sheet中的Menu按钮被点击时,菜单选项会从顶部弹出,但Menu下方明明有可用空间,想咨询是否可以将菜单选项的弹出位置从顶部改为底部。
应用UI截图:
我的代码如下:
Sheet展示的视图
struct EinkaufAddView: View { @State var artikelTextField: String = "" @State var anzahlTextField: String = "" @State var kategorieNamen: String = "Kategorie" var body: some View { NavigationStack { VStack { HStack { VStack { Section("Produkt") { TextField("Hinzufügen...", text: $artikelTextField) .frame(width: 200, height: 50) .padding(.horizontal) .background(Color.gray.opacity(0.3).cornerRadius(10)) .padding(.horizontal) } } VStack { Section("Anzahl") { TextField("0", text: $anzahlTextField) .keyboardType(.numberPad) .frame(height: 50) .frame(maxWidth: .infinity) .padding(.horizontal) .background(Color.gray.opacity(0.3).cornerRadius(10)) .padding(.horizontal) } } } .padding(.bottom, 20) Section("Kategorie") { Menu(kategorieNamen) { Button("Obst") { kategorieNamen = "Obst" } Button("Milchprodukte") { kategorieNamen = "Obst" } Button("Fleisch") { kategorieNamen = "Obst" } Button("Getränke") { kategorieNamen = "Obst" } } .frame(height: 50) .frame(maxWidth: .infinity) .padding(.horizontal) .background(Color.gray.opacity(0.3).cornerRadius(10)) .padding(.horizontal) } } .padding(.top, 30) Spacer() Button(action: { }, label: { Text("Produkt hinzufügen") .foregroundStyle(.white) .font(.title2) .bold() .frame(height: 50) .frame(maxWidth: .infinity) .background(Color.blue.cornerRadius(25)) }) .padding() .navigationTitle("Artikel hinzufügen") } } } #Preview { EinkaufAddView() }
展示Sheet的视图
struct Einkaufsliste: View { @State var showSheet: Bool = false @StateObject var vm = EinkaufViewModel() let kategorien: [Kategorie] = [ Kategorie(title: "Obst", titleColor: Color.yellow, anzahl: 5), Kategorie(title: "Fleisch", titleColor: Color.purple, anzahl: 2), Kategorie(title: "Milchprodukte", titleColor: Color.blue, anzahl: 10), Kategorie(title: "Getränke", titleColor: Color.green, anzahl: 8) ] var body: some View { NavigationStack { ScrollView { HStack { VStack { ForEach(kategorien) { item in NavigationLink { EinkaufslisteDetailView(vm: vm, kategorieName: item.title) } label: { EinkaufsListView(kategorieNamen: item.title, anzahlKategorien: item.anzahl) } .buttonStyle(.plain) .foregroundStyle(item.titleColor) } } } } .navigationBarItems( trailing: Button(action: { showSheet.toggle() }, label: { Image(systemName: "plus.circle") .resizable() .frame(width: 30, height: 30) }) .sheet(isPresented: $showSheet) { EinkaufAddView() .presentationDetents([.fraction(0.8)]) } ) .navigationTitle("Einkaufsliste 🍎") } } } #Preview { Einkaufsliste() } struct EinkaufsListView: View { let kategorieNamen: String let anzahlKategorien: Int var body: some View { ZStack() { RoundedRectangle(cornerRadius: 15) .stroke() HStack { Image(systemName: "apple.logo") .padding(.horizontal) Spacer() Text(kategorieNamen) Spacer() ZStack { RoundedRectangle(cornerRadius: 10) .stroke() .frame(width: 40, height: 40) Text("\(anzahlKategorien)") } Image(systemName: "arrow.right") .padding() } } .font(.title2) .fontWeight(.bold) .frame(width: 350, height: 60) .background(Color.gray.opacity(0.5).cornerRadius(15)) .padding(.top) } }
注:其余代码比较混乱,可忽略。我已经在网上查找相关资料但未找到有用信息,曾听说该设置无法修改,但仍想确认是否有误。
解答
结论
默认的Menu组件确实无法直接修改弹出方向,它的弹出位置由系统自动根据容器空间计算,在Sheet这类受限容器中,系统可能优先选择向上弹出。不过可以通过两种方式实现需求:
方案1:自定义弹出菜单
完全自定义一个类似Menu的组件,通过popover控制弹出方向,这是最可靠的解决方案:
struct CustomMenuView: View { @Binding var selectedCategory: String @State private var isMenuOpen = false let categories = ["Obst", "Milchprodukte", "Fleisch", "Getränke"] var body: some View { Button(action: { isMenuOpen.toggle() }) { Text(selectedCategory) .frame(height: 50) .frame(maxWidth: .infinity) .padding(.horizontal) .background(Color.gray.opacity(0.3).cornerRadius(10)) } .popover(isPresented: $isMenuOpen, attachmentAnchor: .point(.bottom), arrowEdge: .top) { VStack(spacing: 0) { ForEach(categories, id: \.self) { category in Button(action: { selectedCategory = category isMenuOpen = false }) { Text(category) .frame(maxWidth: .infinity, minHeight: 44) .padding(.horizontal) } } } .background(Color.white) .cornerRadius(10) .shadow(radius: 5) } } }
在EinkaufAddView中替换原有Menu:
Section("Kategorie") { CustomMenuView(selectedCategory: $kategorieNamen) .padding(.horizontal) }
方案2:尝试修改MenuStyle(效果有限)
虽然无法直接指定方向,但可以尝试使用BorderlessButtonMenuStyle,不过这种方式仍受系统布局限制,不一定能完全满足需求:
Menu(kategorieNamen) { // 菜单按钮内容 } .menuStyle(BorderlessButtonMenuStyle())
补充说明
系统默认的Menu基于UIKit菜单实现,SwiftUI并未暴露修改弹出方向的API,因此自定义组件是实现需求的最优解。
内容的提问来源于stack exchange,提问作者TheRiser
相关产品推荐
相关产品推荐

