使用 SwiftUI 中的 Menu 组件添加点击菜单

了解如何使用 SwiftUI 中的 Menu 组件为你的 iOS 应用添加点击菜单,并自定义创建不同的菜单效果。

使用 SwiftUI 中的 Menu 组件添加点击菜单

在应用开发过程中,往往需要为界面中的元素添加菜单选项,以便提供更多的操作和交互方式。在此之前,开发者通常需要通过自定义视图或使用多个 UIKit 组件来实现这些功能。

iOS 14 开始,SwiftUI 引入了专门的 Menu组件,简化了这一功能的实现。

Menu 和 ContextMenu 区别

SwiftUI 中提供了两种菜单组件——MenuContextMenu 。他们最明显的区别是:Menu 组件通过点击触发,ContextMenu 则通过长按触发。

适合 Menu 的使用场景:

  • 工具栏中的操作选项:当用户在导航栏或工具栏中需要一组操作时,可以用 Menu 来展示多项选择。
  • 按钮上的操作:例如在应用的某个视图上有一个“更多”按钮,点击这个按钮后会弹出多个操作选项,这种情况下适合使用 Menu

适合 ContextMenu 的使用场景

ContextMenu 适合用于“上下文菜单”场景,即针对某个特定元素或视图的操作

  • 文件管理应用中的操作:当用户长按某个文件或照片时,可以显示“复制”“删除”“分享”等选项。
  • 列表项的操作:在列表应用中,当用户长按一个列表项时,ContextMenu 可显示对该项的操作,如“标记为已读”“标记为重要”等。
特性 Menu ContextMenu
触发方式 点击触发 长按(iOS)/右键(macOS)触发
操作对象 通常是一般性的操作或导航栏按钮的操作 针对特定视图的上下文操作
嵌套支持 支持嵌套菜单,适合多级操作 不支持嵌套,操作通常简单直接
适用场景 工具栏菜单、分组操作 文件操作、上下文敏感的功能

Menu 组件基本用法

首先,让我们来看看如何创建一个最基本的 Menu 组件。

import SwiftUI

struct DocumentMenu: View {
    var body: some View {
        Menu {
            Button(action: {
                // 导出 PDF 操作
            }) {
                Label("导出为 PDF", systemImage: "doc.fill")
            }

            Button(action: {
                // 打印操作
            }) {
                Label("打印文档", systemImage: "printer")
            }

            Divider()

            Button(
                role: .destructive,
                action: {
                    // 删除操作
                }
            ) {
                Label("删除文档", systemImage: "trash")
            }
        } label: {
            Image(systemName: "ellipsis.circle")
                .font(.title2)
        }
    }
}

#Preview {
    DocumentMenu()
        .previewLayout(.sizeThatFits)
        .padding()
}

由于 Ghost 平台对图片压缩的原因,导致上述 GIF 动图中 Menu 菜单背景框显示异常,实际上的效果如下:

单独使用 Label 组件时,图标默认在文字左侧显示。不过在 Menu 中使用 Label 组件,图标会被调整到文字右侧显示。

创建 Menu 创建嵌套菜单

有时,一个菜单项可能包含多个子项,这时可以使用嵌套的 Menu