View — ToolbarTitleMenu

ToolbarTitleMenu 是 SwiftUI 提供的一个视图修饰符,用于在导航标题上添加下拉菜单。点击标题时会显示一个包含各种选项的菜单。

1. 基本用法 #

NavigationStack {
    List {
        Text("内容")
    }
    .navigationTitle("主页")
    .toolbarTitleMenu {
        Button("选项 1") {
            print("选项 1 被点击")
        }
        Button("选项 2") {
            print("选项 2 被点击")
        }
        Button("选项 3") {
            print("选项 3 被点击")
        }
    }
}

2. 实用示例 #

示例 1:文档版本切换 #

struct DocumentView: View {
    @State private var currentVersion = "1.0"
    
    var body: some View {
        NavigationStack {
            List {
                Text("文档内容")
            }
            .navigationTitle("文档 v\(currentVersion)")
            .toolbarTitleMenu {
                Button("版本 1.0") {
                    currentVersion = "1.0"
                }
                Button("版本 2.0") {
                    currentVersion = "2.0"
                }
                Button("版本 3.0") {
                    currentVersion = "3.0"
                }
                
                Divider()
                
                Button("查看历史版本") {
                    // 显示历史版本列表
                }
            }
        }
    }
}

示例 2:带图标和分组的菜单 #

struct ContentView: View {
    @State private var selectedFilter = "全部"
    
    var body: some View {
        NavigationStack {
            List {
                Text("内容列表")
            }
            .navigationTitle("筛选: \(selectedFilter)")
            .toolbarTitleMenu {
                Group {
                    Text("选择筛选条件")
                        .font(.caption)
                        .foregroundColor(.gray)
                    
                    Divider()
                }
                
                Button {
                    selectedFilter = "全部"
                } label: {
                    Label("全部", systemImage: "list.bullet")
                }
                
                Button {
                    selectedFilter = "收藏"
                } label: {
                    Label("收藏", systemImage: "star.fill")
                }
                
                Button {
                    selectedFilter = "未读"
                } label: {
                    Label("未读", systemImage: "circle.fill")
                }
            }
        }
    }
}

示例 3:结合异步操作 #

struct ProjectView: View {
    @State private var currentProject = "项目 A"
    @State private var isLoading = false
    
    var body: some View {
        NavigationStack {
            List {
                Text("项目内容")
            }
            .navigationTitle(currentProject)
            .toolbarTitleMenu {
                // 项目选择
                ForEach(["项目 A", "项目 B", "项目 C"], id: \.self) { project in
                    Button {
                        switchProject(to: project)
                    } label: {
                        HStack {
                            Text(project)
                            if project == currentProject {
                                Image(systemName: "checkmark")
                            }
                        }
                    }
                }
                
                Divider()
                
                // 项目管理选项
                Button {
                    // 创建新项目
                } label: {
                    Label("新建项目", systemImage: "plus")
                }
                
                Button(role: .destructive) {
                    // 删除当前项目
                } label: {
                    Label("删除项目", systemImage: "trash")
                }
            }
            .overlay(Group {
                if isLoading {
                    ProgressView()
                }
            })
        }
    }
    
    func switchProject(to project: String) {
        isLoading = true
        // 模拟异步加载
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            currentProject = project
            isLoading = false
        }
    }
}

示例 4:多级菜单 #

struct FileView: View {
    @State private var currentFile = "文档.txt"
    
    var body: some View {
        NavigationStack {
            List {
                Text("文件内容")
            }
            .navigationTitle(currentFile)
            .toolbarTitleMenu {
                Menu("最近文件") {
                    Button("文档1.txt") {
                        currentFile = "文档1.txt"
                    }
                    Button("文档2.txt") {
                        currentFile = "文档2.txt"
                    }
                }
                
                Menu("收藏夹") {
                    Button("重要文档.txt") {
                        currentFile = "重要文档.txt"
                    }
                    Button("笔记.txt") {
                        currentFile = "笔记.txt"
                    }
                }
                
                Divider()
                
                Button("打开文件...") {
                    // 显示文件选择器
                }
            }
        }
    }
}

示例 5:状态切换和确认对话框 #

struct TaskView: View {
    @State private var currentStatus = "进行中"
    @State private var showConfirmation = false
    @State private var pendingStatus: String?
    
    var body: some View {
        NavigationStack {
            List {
                Text("任务详情")
            }
            .navigationTitle("状态: \(currentStatus)")
            .toolbarTitleMenu {
                ForEach(["待处理", "进行中", "已完成", "已取消"], id: \.self) { status in
                    Button {
                        pendingStatus = status
                        if status == "已取消" {
                            showConfirmation = true
                        } else {
                            currentStatus = status
                        }
                    } label: {
                        Label(
                            status,
                            systemImage: status == currentStatus ? "checkmark.circle.fill" : "circle"
                        )
                    }
                }
            }
            .alert("确认取消", isPresented: $showConfirmation) {
                Button("确定", role: .destructive) {
                    if let status = pendingStatus {
                        currentStatus = status
                    }
                }
                Button("取消", role: .cancel) { }
            } message: {
                Text("确定要取消这个任务吗?")
            }
        }
    }
}

3. 使用提示 #

  1. 菜单内容组织

    • 使用 Divider() 分隔不同类型的选项
    • 使用 Group 组织相关选项
    • 可以添加图标增加可视性
  2. 交互反馈

    • 可以显示当前选中状态
    • 可以添加加载状态
    • 可以使用确认对话框
  3. 最佳实践

    • 保持菜单项数量适中
    • 确保选项描述清晰
    • 考虑添加快捷键支持(如果适用)
    • 对危险操作添加确认步骤
  4. 注意事项

    • 需要在 NavigationStack/NavigationView 中使用
    • 菜单项不宜过多
    • 考虑操作的可撤销性

通过这些示例,你可以看到 ToolbarTitleMenu 非常适合用于:

  • 切换视图状态
  • 过滤内容
  • 版本选择
  • 快速操作
  • 分层导航

选择合适的使用场景,可以让你的应用更加易用和高效。

本文共 1808 字,上次修改于 Jan 4, 2025