Qualities of a great Mac Catalyst app
Design 入门 20m

优秀 Mac Catalyst app 的品质

Qualities of a great Mac Catalyst app

2021年6月9日

在 Apple 官方观看视频

一句话判断

评判一个 Catalyst app 是否「够格」的标准很简单——打开 app 后,如果用户感觉不出这是一个从 iPad 移植过来的 app,你就成功了。

这场 Session 讲了什么

这场 Session 是 “What’s new in Mac Catalyst” 的设计篇。它不讲 API,而是讲设计原则。Apple 总结了优秀的 Mac Catalyst app 应该具备的五个品质。

窗口管理:Mac app 应该支持多窗口、全屏模式和窗口大小调整。Catalyst app 经常忽略窗口大小变化,导致内容被拉伸或者裁切。正确做法是用 Auto Layout 和 Size Class 来适配不同的窗口尺寸。

键盘优先:Mac 用户期望所有操作都能通过键盘完成。Catalyst app 不能只依赖触摸/点击交互,必须为所有核心功能提供键盘快捷键。

标准菜单:Mac 的菜单栏是系统级的 UI 组件。Catalyst app 应该通过 UIMenuUIKeyCommand 把主要功能暴露到菜单栏中,而不是把所有功能都藏在 app 内部的 hamburger 菜单里。

拖拽支持:Mac 用户习惯在 app 之间拖拽内容(文本、图片、文件)。Catalyst app 应该实现 UIDragInteractionUIDropInteraction

原生外观:字体大小、间距、控件样式都应该接近原生 Mac app 的视觉规范。iPad 的触摸友好大按钮在 Mac 上看起来会特别笨重。

值得深挖的点

窗口大小适配的常见错误

最常见的问题是固定宽度布局。iPad app 通常假设屏幕宽度在 320-1024pt 之间,但 Mac 窗口可以拉到 2560pt 甚至更宽。如果你的布局在超宽窗口下变成了一个横向拉伸的怪物,用户体验会非常糟糕。建议设置 window?.minSizemaxSize 来限制窗口尺寸范围,或者用多列布局来填充宽屏空间。

菜单栏的设计策略

Mac 的菜单栏是层级式的:File、Edit、View、Window、Help 是标准菜单。Catalyst app 应该遵循这个结构,而不是把所有功能塞进一个「更多」菜单。UIMenu 支持 identifier 来指定菜单的层级位置。

代码片段

// 配置 Mac 风格的菜单栏
import UIKit

class MainViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setupMenus()
    }
    
    func setupMenus() {
        // 文件菜单
        let fileMenu = UIMenu(
            title: "文件",
            identifier: .file,
            options: .displayInline,
            children: [
                UIKeyCommand(title: "新建", action: #selector(newFile), input: "n", modifierFlags: .command),
                UIKeyCommand(title: "打开", action: #selector(openFile), input: "o", modifierFlags: .command),
                .separator(),
                UIKeyCommand(title: "保存", action: #selector(saveFile), input: "s", modifierFlags: .command),
            ]
        )
        
        // 编辑菜单
        let editMenu = UIMenu(
            title: "编辑",
            identifier: .edit,
            children: [
                UIKeyCommand(title: "撤销", action: #selector(undo), input: "z", modifierFlags: .command),
                UIKeyCommand(title: "重做", action: #selector(redo), input: "z", modifierFlags: [.command, .shift]),
            ]
        )
        
        // 设置为主菜单
        let mainMenu = UIMenu(
            title: "",
            children: [fileMenu, editMenu]
        )
        
        // Catalyst 会自动将这些菜单放入 Mac 菜单栏
    }
}
// 适配不同窗口尺寸
class AdaptiveLayoutViewController: UIViewController {
    var contentView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 使用 Auto Layout 适配任意窗口大小
        contentView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(contentView)
        
        NSLayoutConstraint.activate([
            contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            contentView.topAnchor.constraint(equalTo: view.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])
        
        // 监听窗口大小变化
        NotificationCenter.default.addObserver(
            forName: UIWindowScene.didEnterBackgroundNotification,
            object: nil, queue: .main
        ) { _ in
            self.updateLayout()
        }
    }
    
    func updateLayout() {
        let isWide = view.frame.width > 900
        // 根据宽度切换布局
    }
}

最佳实践

UIStatusBarStyleUINavigationBarAppearance 来定制导航栏的外观,让它更接近 Mac 的工具栏风格。iPad 风格的大标题导航栏在 Mac 上通常显得过大,建议使用 compact 风格。

测试时一定要在全屏、半屏、最小窗口三种尺寸下都检查一遍。很多 Catalyst 的视觉问题只会在极端窗口尺寸下暴露。

还有什么值得关注

  • Mac Catalyst app 支持通过 NSAppearance API 来精确控制深色/浅色模式。
  • touchBar API 在 Catalyst 中可用,可以为 MacBook Pro 的 Touch Bar 提供自定义内容。
  • Catalyst app 的 Help 菜单可以通过 UIApplication.shared.connectToHost 注册到系统的 Help 搜索中。
WWDC 2021