优秀 Mac Catalyst app 的品质
Qualities of a great Mac Catalyst app
2021年6月9日
一句话判断
评判一个 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 应该通过 UIMenu 和 UIKeyCommand 把主要功能暴露到菜单栏中,而不是把所有功能都藏在 app 内部的 hamburger 菜单里。
拖拽支持:Mac 用户习惯在 app 之间拖拽内容(文本、图片、文件)。Catalyst app 应该实现 UIDragInteraction 和 UIDropInteraction。
原生外观:字体大小、间距、控件样式都应该接近原生 Mac app 的视觉规范。iPad 的触摸友好大按钮在 Mac 上看起来会特别笨重。
值得深挖的点
窗口大小适配的常见错误
最常见的问题是固定宽度布局。iPad app 通常假设屏幕宽度在 320-1024pt 之间,但 Mac 窗口可以拉到 2560pt 甚至更宽。如果你的布局在超宽窗口下变成了一个横向拉伸的怪物,用户体验会非常糟糕。建议设置 window?.minSize 和 maxSize 来限制窗口尺寸范围,或者用多列布局来填充宽屏空间。
菜单栏的设计策略
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
// 根据宽度切换布局
}
}
最佳实践
用 UIStatusBarStyle 和 UINavigationBarAppearance 来定制导航栏的外观,让它更接近 Mac 的工具栏风格。iPad 风格的大标题导航栏在 Mac 上通常显得过大,建议使用 compact 风格。
测试时一定要在全屏、半屏、最小窗口三种尺寸下都检查一遍。很多 Catalyst 的视觉问题只会在极端窗口尺寸下暴露。
还有什么值得关注
- Mac Catalyst app 支持通过
NSAppearanceAPI 来精确控制深色/浅色模式。 touchBarAPI 在 Catalyst 中可用,可以为 MacBook Pro 的 Touch Bar 提供自定义内容。- Catalyst app 的 Help 菜单可以通过
UIApplication.shared.connectToHost注册到系统的 Help 搜索中。