Build with iOS pickers, menus and actions
Swift & UI 入门 24m

使用 iOS 选择器、菜单和操作

Build with iOS pickers, menus and actions

2020年6月26日

在 Apple 官方观看视频

一句话判断

iOS 14 给 UIKit 和 SwiftUI 都带来了更丰富的交互控件——Menus、Pickers、和 UIActions 的组合让复杂的操作可以用更少的代码实现,这场 Session 是这些新控件的速查手册。

这场 Session 讲了什么

iOS 14 新增了多个交互控件和 API,Session 将它们分为三大类:

Menus 是最重要的新增控件。UIMenu 可以附加到 Button、Bar Button Item、甚至作为 TableView/CollectionView Cell 的上下文菜单。UIButton 新增了 menu 属性,点击按钮时弹出菜单而不是执行单一操作。长按手势自动触发菜单,第一次点击直接执行菜单的第一个选项(这种行为叫”primary action”)。

Pickers 在 iOS 14 中有了新的内联展示方式。PHPickerViewController(照片选择器)替代了旧的 UIImagePickerController,支持多选和搜索。UIColorPickerViewController 是全新的颜色选择器,支持从色轮、色板、铅笔输入中选择颜色。

UIActions 贯穿所有新控件。UIAlertAction 的概念扩展到了按钮、菜单项、上下文菜单等场景,统一的 handler 回调模式简化了事件处理代码。

值得深挖的点

UIMenu 的 Primary Action 模式。 给 UIButton 配置 menu 时,按钮会显示一个下拉箭头暗示有菜单。短按执行 primaryAction(如果配置了的话),长按显示完整菜单。这种模式非常适合”保存”按钮——点击直接保存为默认格式,长按可以选择其他格式。

UIColorPickerViewController 的回调设计。 颜色选择器通过 delegate 方法提供实时更新——用户每拖动一次色轮都会触发 colorPickerViewController(_:didSelect:)。这意味着你可以实时预览颜色变化,而不需要等用户确认。

代码片段

import UIKit

// UIMenu 附加到 UIButton
let saveMenu = UIMenu(
    title: "保存选项",
    children: [
        UIAction(title: "保存为 PDF", image: UIImage(systemName: "doc.richtext")) { _ in
            exportAsPDF()
        },
        UIAction(title: "保存为图片", image: UIImage(systemName: "photo")) { _ in
            exportAsImage()
        },
        UIMenu(title: "分享", options: .displayInline, children: [
            UIAction(title: "通过邮件", image: UIImage(systemName: "envelope")) { _ in
                shareViaEmail()
            }
        ])
    ]
)

let saveButton = UIButton(type: .system)
saveButton.setTitle("保存", for: .normal)
saveButton.menu = saveMenu
saveButton.showsMenuAsPrimaryAction = true  // 点击即显示菜单
// UIColorPickerViewController——颜色选择器
class ColorPickerViewController: UIViewController, UIColorPickerViewControllerDelegate {
    var selectedColor: UIColor = .systemBlue
    
    @IBAction func pickColor() {
        let picker = UIColorPickerViewController()
        picker.selectedColor = selectedColor
        picker.supportsAlpha = true  // 允许选择透明度
        picker.delegate = self
        present(picker, animated: true)
    }
    
    // 实时回调——用户每改变一次颜色就触发
    func colorPickerViewController(
        _ viewController: UIColorPickerViewController,
        didSelect color: UIColor
    ) {
        selectedColor = color
        previewView.backgroundColor = color
    }
    
    // 用户点击确认后的回调
    func colorPickerViewControllerDidFinish(
        _ viewController: UIColorPickerViewController
    ) {
        print("最终选择的颜色: \(viewController.selectedColor)")
    }
}
// SwiftUI 中的 Menu 和 Picker
struct ContentView: View {
    @State private var sortOrder = "名称"
    @State private var selectedColor: Color = .blue
    
    var body: some View {
        VStack {
            // Menu
            Menu("排序") {
                Button("按名称") { sortOrder = "名称" }
                Button("按日期") { sortOrder = "日期" }
                Button("按大小") { sortOrder = "大小" }
            }
            
            // ColorPicker(SwiftUI 原生支持)
            ColorPicker("选择颜色", selection: $selectedColor)
                .padding()
        }
    }
}

最佳实践

  • 用 UIMenu 替代 ActionSheet。 当选项少于 5 个时,Menu 比 ActionSheet 更轻量、更直观。
  • 按钮的 Menu 要配置 primaryAction。 如果用户 90% 的情况都选第一个选项,让短按直接执行第一个操作,长按才显示菜单。
  • UIColorPicker 的实时回调中只做轻量更新。 不要在每次颜色变化时都保存到数据库,等用户确认后再持久化。
  • SwiftUI 的 Menu 和 Picker 配合使用。 Menu 处理操作,Picker 处理选择,两者的 API 设计很统一。

还有什么值得关注

  • UIMenu 支持嵌套菜单,但不要超过两层
  • UIAction 支持 attributes(.destructive)来标记危险操作为红色
  • PHPickerViewController 支持多选和类型过滤,是新项目推荐的照片选择方案
  • Bar Button Item 也支持直接附加 UIMenu
WWDC 2020