Menu 视图在 iOS 和 macOS 上都有良好的支持,并且可以根据平台自动适配。在 macOS 上, Menu 自动呈现为下拉按钮。
Menu
视图主要用于显示按钮的弹出菜单,以便在用户点击按钮时显示一组选项。这些选项可以是用于执行特定操作的按钮,也可以是用于选择不同选项的选择器。Menu
视图的主要用途包括:
Menu
视图可以显示一组按钮或选择器,用于执行次要操作或选择不同的选项。这些选项可以是用于创建新文件或文件夹的按钮,也可以是用于对文件进行排序的选择器。Menu
视图可以帮助提供额外的上下文,以解决用户在执行主要操作时可能遇到的歧义。例如,在文件管理应用程序中,用户可能需要选择是创建新文件还是新文件夹,Menu
视图可以帮助提供额外的上下文,以便用户做出明智的选择。// 1.当点击 B 的时候,就会展示 A
Menu {
// 这里是 Menu 展开以后的内容 A
} label: {
// 这里是 Menu 未展开时的界面 B
}
// 2.未展开时显示个字符串,展开后显示3个按钮
Menu("Options") {
Button("Order Now", action: placeOrder)
Button("Adjust Order", action: adjustOrder)
Button("Cancel", action: cancelOrder)
}
// 还可以将菜单放置在菜单中,所谓嵌套。这将导致二级菜单在最初会折叠起来,点击后再展开:
struct ContentView: View {
var body: some View {
Menu("Options") {
Button("Order Now", action: placeOrder)
Button("Adjust Order", action: adjustOrder)
Menu("Advanced") {
Button("Rename", action: rename)
Button("Delay", action: delay)
}
Button("Cancel", action: cancelOrder)
}
}
func placeOrder() { }
func adjustOrder() { }
func rename() { }
func delay() { }
func cancelOrder() { }
}
在 iOS 15 及更高版本中,菜单还可以设置一个 primary action
。设置完成后,当点击菜单按钮就会执行 primary action
;如果不是点击,是长按住就可以展现完整的菜单选项。
struct ContentView: View {
var body: some View {
Menu("Options") {
Button("Order Now", action: placeOrder)
Button("Adjust Order", action: adjustOrder)
Button("Cancel", action: cancelOrder)
} primaryAction: {
justDoIt()
}
}
func justDoIt() {
print("Button was tapped")
}
func placeOrder() { }
func adjustOrder() { }
func cancelOrder() { }
}
您可以将 Menu 视图与按钮一起使用,以便在用户点击按钮时显示菜单。这可以通过在按钮上使用 .menu
修饰符来实现。当用户点击按钮时,Menu视图将显示相关的选项供用户选择。
Button(action: {
// Your action
}) {
Text("Show Menu")
.menu {
// Menu content
}
}
您可以根据特定条件来触发 Menu 视图的显示。这可以通过使用 @State
或 @Binding
来控制 Menu 视图的显示与隐藏。
@State private var isMenuVisible = false
Button(action: {
isMenuVisible.toggle()
}) {
Text("Show Menu")
}
.menu(isPresented: $isMenuVisible) {
// Menu content
}
SwiftUI 为我们提供了 ContextMenu
修饰符,用于在应用程序中创建弹出菜单。在 iOS 中,这通常是通过长按触发的;在较旧的 iPhone 上,用户可以通过用力按压某物来触发 3D Touch;但它的工作原理与 macOS 上的右键单击相同 - 这是一个灵活的 API。
以下提示,能在你使用上下文菜单时,帮助确保为用户提供最佳体验:
上下文菜单是由一组按钮构建的,每个按钮都有自己的操作、文本和图标。文本和图标可以直接在按钮内部提供,因为 SwiftUI 将提供隐式 HStack
以确保它们符合系统标准外观和感觉。我们可以构建一个简单的上下文菜单来控制视图的背景颜色,如下所示:
Text("Change Color")
.padding()
.contextMenu {
Button("Red") { backgroundColor = .red }
Button("Green") { backgroundColor = .green }
Button("Blue") { backgroundColor = .blue }
}
//像 TabView 一样,上下文菜单中的每个项目都可以使用 Label 视图附加文本和图像
Text("Options")
.contextMenu {
Button {
print("Change country setting")
} label: {
Label("Choose Country", systemImage: "globe")
}
Button {
print("Enable geolocation")
} label: {
Label("Detect Location", systemImage: "location.circle")
}
}
contextMenu
有个问题:为了保持应用程序之间的界面统一,iOS 会将每个图像渲染为纯色,并保留不透明度。这使得许多图片毫无用处:
// 例如您有三张照片用在了 contextMenu 中,则所有三张照片都会呈现为纯黑色正方形,因为所有颜色都被删除了
// 因此,你应该使用线条图标作为替代,例如 Apple 的 SF Symbols
Button("Red", systemImage: "checkmark.circle.fill") {
backgroundColor = .red
}
//另外,如果使用 foregroundStyle() 修饰符,会发现无效(iOS 希望菜单看起来统一,所以尝试随机给它们着色是行不通的)
.foregroundStyle(.red)
//如果您确实希望该项目显示为红色(您应该知道这意味着破坏性),应该使用按钮角色去声明
Button("Red", systemImage: "checkmark.circle.fill", role: .destructive) {
backgroundColor = .red
}
contextMenu
指的是通过长按元素弹出的菜单。
// 首先定义删除方法,加到body外面
func delete(item restaurant: Restaurant){
//闭包中的 $0 代表 restaurants 数组中的一个个元素
if let index = self.restaurants.firstIndex(where: { $0.id == restaurant.id }){
self.restaurants.remove(at: index)
}
}
// 设置
var body: some View {
ForEach(restaurants) { restaurant in
BasicImageRow(restaurant: restaurant)
// 跟 onDelete 處理器不同的是,這個 contextMenu 並不会提供所选餐厅的索引。要設置它還需要多進行一點工作。
.contextMenu {
Button(action: {
delete(item: restaurant)
}){
HStack{
Text("Delete")
Image(systemName: "trash")
}
}
}
}
}
例子:‣