macos-design-guidelines
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesemacOS Human Interface Guidelines
macOS 人机界面指南
Mac apps serve power users who expect deep keyboard control, persistent menu bars, resizable multi-window layouts, and tight system integration. These guidelines codify Apple's HIG into actionable rules with SwiftUI and AppKit examples.
Mac应用面向的是对键盘深度控制、持久化菜单栏、可调整大小的多窗口布局以及紧密系统集成有需求的高级用户。本指南将Apple的HIG(人机界面指南)整理为可执行的规则,并附带SwiftUI和AppKit示例。
1. Menu Bar (CRITICAL)
1. 菜单栏(关键要求)
Every Mac app must have a menu bar. It is the primary discovery mechanism for commands. Users who cannot find a feature will look in the menu bar before anywhere else.
每个Mac应用都必须有菜单栏,它是用户查找命令的主要途径。用户如果找不到某个功能,首先会查看菜单栏。
Rule 1.1 — Provide Standard Menus
规则1.1 — 提供标准菜单
Every app must include at minimum: App, File, Edit, View, Window, Help. Omit File only if the app is not document-based. Add app-specific menus between Edit and View or between View and Window.
swift
// SwiftUI — Standard menu structure
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
// Adds to existing standard menus
CommandGroup(after: .newItem) {
Button("New from Template...") { newFromTemplate() }
.keyboardShortcut("T", modifiers: [.command, .shift])
}
CommandMenu("Canvas") {
Button("Zoom to Fit") { zoomToFit() }
.keyboardShortcut("0", modifiers: .command)
Divider()
Button("Add Artboard") { addArtboard() }
.keyboardShortcut("A", modifiers: [.command, .shift])
}
}
}
}swift
// AppKit — Building menus programmatically
let editMenu = NSMenu(title: "Edit")
let undoItem = NSMenuItem(title: "Undo", action: #selector(UndoManager.undo), keyEquivalent: "z")
let redoItem = NSMenuItem(title: "Redo", action: #selector(UndoManager.redo), keyEquivalent: "Z")
editMenu.addItem(undoItem)
editMenu.addItem(redoItem)
editMenu.addItem(.separator())每个应用至少必须包含:应用、文件、编辑、视图、窗口、帮助菜单。如果应用不是基于文档的,可以省略“文件”菜单。在“编辑”和“视图”之间,或“视图”和“窗口”之间添加应用专属菜单。
swift
// SwiftUI — 标准菜单结构
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
// 向现有标准菜单添加内容
CommandGroup(after: .newItem) {
Button("New from Template...") { newFromTemplate() }
.keyboardShortcut("T", modifiers: [.command, .shift])
}
CommandMenu("Canvas") {
Button("Zoom to Fit") { zoomToFit() }
.keyboardShortcut("0", modifiers: .command)
Divider()
Button("Add Artboard") { addArtboard() }
.keyboardShortcut("A", modifiers: [.command, .shift])
}
}
}
}swift
// AppKit — 以编程方式构建菜单
let editMenu = NSMenu(title: "Edit")
let undoItem = NSMenuItem(title: "Undo", action: #selector(UndoManager.undo), keyEquivalent: "z")
let redoItem = NSMenuItem(title: "Redo", action: #selector(UndoManager.redo), keyEquivalent: "Z")
editMenu.addItem(undoItem)
editMenu.addItem(redoItem)
editMenu.addItem(.separator())Rule 1.2 — Keyboard Shortcuts for All Menu Items
规则1.2 — 为所有菜单项设置键盘快捷键
Every menu item that performs an action must have a keyboard shortcut. Use standard shortcuts for standard actions (Cmd+C, Cmd+V, Cmd+Z, etc.). Custom shortcuts should use Cmd plus a letter. Reserve Cmd+Shift, Cmd+Option, and Cmd+Ctrl combos for secondary actions.
Standard Shortcut Reference:
| Action | Shortcut |
|---|---|
| New | Cmd+N |
| Open | Cmd+O |
| Close | Cmd+W |
| Save | Cmd+S |
| Save As | Cmd+Shift+S |
| Cmd+P | |
| Undo | Cmd+Z |
| Redo | Cmd+Shift+Z |
| Cut | Cmd+X |
| Copy | Cmd+C |
| Paste | Cmd+V |
| Select All | Cmd+A |
| Find | Cmd+F |
| Find Next | Cmd+G |
| Preferences/Settings | Cmd+, |
| Hide App | Cmd+H |
| Quit | Cmd+Q |
| Minimize | Cmd+M |
| Fullscreen | Cmd+Ctrl+F |
每个执行操作的菜单项都必须有键盘快捷键。标准操作使用标准快捷键(Cmd+C、Cmd+V、Cmd+Z等)。自定义快捷键应使用Cmd加字母组合。Cmd+Shift、Cmd+Option和Cmd+Ctrl组合保留给次要操作使用。
标准快捷键参考:
| 操作 | 快捷键 |
|---|---|
| 新建 | Cmd+N |
| 打开 | Cmd+O |
| 关闭 | Cmd+W |
| 保存 | Cmd+S |
| 另存为 | Cmd+Shift+S |
| 打印 | Cmd+P |
| 撤销 | Cmd+Z |
| 重做 | Cmd+Shift+Z |
| 剪切 | Cmd+X |
| 复制 | Cmd+C |
| 粘贴 | Cmd+V |
| 全选 | Cmd+A |
| 查找 | Cmd+F |
| 查找下一个 | Cmd+G |
| 偏好设置/设置 | Cmd+, |
| 隐藏应用 | Cmd+H |
| 退出 | Cmd+Q |
| 最小化 | Cmd+M |
| 全屏 | Cmd+Ctrl+F |
Rule 1.3 — Dynamic Menu Updates
规则1.3 — 动态更新菜单
Menu items must reflect current state. Disable items that are not applicable. Update titles to match context (e.g., "Undo Typing" not just "Undo"). Toggle checkmarks for on/off states.
swift
// SwiftUI — Dynamic menu state
CommandGroup(replacing: .toolbar) {
Button(showingSidebar ? "Hide Sidebar" : "Show Sidebar") {
showingSidebar.toggle()
}
.keyboardShortcut("S", modifiers: [.command, .control])
}swift
// AppKit — Validate menu items
override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
if menuItem.action == #selector(delete(_:)) {
menuItem.title = selectedItems.count > 1 ? "Delete \(selectedItems.count) Items" : "Delete"
return !selectedItems.isEmpty
}
return super.validateMenuItem(menuItem)
}菜单项必须反映当前状态。禁用不适用的菜单项。根据上下文更新标题(例如,显示“撤销输入”而非仅“撤销”)。为开关状态添加复选标记。
swift
// SwiftUI — 动态菜单状态
CommandGroup(replacing: .toolbar) {
Button(showingSidebar ? "Hide Sidebar" : "Show Sidebar") {
showingSidebar.toggle()
}
.keyboardShortcut("S", modifiers: [.command, .control])
}swift
// AppKit — 验证菜单项
override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
if menuItem.action == #selector(delete(_:)) {
menuItem.title = selectedItems.count > 1 ? "Delete \(selectedItems.count) Items" : "Delete"
return !selectedItems.isEmpty
}
return super.validateMenuItem(menuItem)
}Rule 1.4 — Contextual Menus
规则1.4 — 上下文菜单
Provide right-click context menus on all interactive elements. Context menus should contain the most relevant subset of menu bar actions for the clicked element, plus element-specific actions.
swift
// SwiftUI
Text(item.name)
.contextMenu {
Button("Rename...") { rename(item) }
Button("Duplicate") { duplicate(item) }
Divider()
Button("Delete", role: .destructive) { delete(item) }
}为所有交互元素提供右键上下文菜单。上下文菜单应包含与点击元素最相关的菜单栏操作子集,以及元素专属操作。
swift
// SwiftUI
Text(item.name)
.contextMenu {
Button("Rename...") { rename(item) }
Button("Duplicate") { duplicate(item) }
Divider()
Button("Delete", role: .destructive) { delete(item) }
}Rule 1.5 — App Menu Structure
规则1.5 — 应用菜单结构
The App menu (leftmost, bold app name) must contain: About, Preferences/Settings (Cmd+,), Services submenu, Hide App (Cmd+H), Hide Others (Cmd+Option+H), Show All, Quit (Cmd+Q). Never rename or remove these standard items.
swift
// SwiftUI — Settings scene
@main
struct MyApp: App {
var body: some Scene {
WindowGroup { ContentView() }
Settings { SettingsView() } // Automatically wired to Cmd+,
}
}应用菜单(最左侧的粗体应用名称)必须包含:关于、偏好设置/设置(Cmd+,)、服务子菜单、隐藏应用(Cmd+H)、隐藏其他应用(Cmd+Option+H)、显示全部、退出(Cmd+Q)。请勿重命名或移除这些标准项。
swift
// SwiftUI — 设置场景
@main
struct MyApp: App {
var body: some Scene {
WindowGroup { ContentView() }
Settings { SettingsView() } // 自动关联到Cmd+,
}
}2. Windows (CRITICAL)
2. 窗口(关键要求)
Mac users expect full control over window size, position, and lifecycle. An app that fights window management feels fundamentally broken on the Mac.
Mac用户期望完全控制窗口的大小、位置和生命周期。抗拒窗口管理的应用在Mac上会显得完全不合格。
Rule 2.1 — Resizable with Sensible Minimums
规则2.1 — 可调整大小并设置合理的最小值
All main windows must be freely resizable. Set a minimum size that keeps the UI usable. Never set a maximum size unless the content truly cannot scale (rare).
swift
// SwiftUI
WindowGroup {
ContentView()
.frame(minWidth: 600, minHeight: 400)
}
.defaultSize(width: 900, height: 600)swift
// AppKit
window.minSize = NSSize(width: 600, height: 400)
window.setContentSize(NSSize(width: 900, height: 600))所有主窗口必须可自由调整大小。设置一个能保持UI可用性的最小尺寸。除非内容确实无法缩放(这种情况很少见),否则不要设置最大尺寸。
swift
// SwiftUI
WindowGroup {
ContentView()
.frame(minWidth: 600, minHeight: 400)
}
.defaultSize(width: 900, height: 600)swift
// AppKit
window.minSize = NSSize(width: 600, height: 400)
window.setContentSize(NSSize(width: 900, height: 600))Rule 2.2 — Support Fullscreen and Split View
规则2.2 — 支持全屏和分屏视图
Opt into native fullscreen by setting the appropriate window collection behavior. The green traffic-light button must either enter fullscreen or show the tile picker.
swift
// AppKit
window.collectionBehavior.insert(.fullScreenPrimary)SwiftUI windows get fullscreen support automatically.
通过设置适当的窗口集合行为来启用原生全屏功能。绿色交通灯按钮必须能够进入全屏或显示分屏选择器。
swift
// AppKit
window.collectionBehavior.insert(.fullScreenPrimary)SwiftUI窗口自动获得全屏支持。
Rule 2.3 — Multiple Windows
规则2.3 — 多窗口支持
Unless your app is a single-purpose utility, support multiple windows. Document-based apps must allow multiple documents open simultaneously. Use or in SwiftUI.
WindowGroupDocumentGroupswift
// SwiftUI — Document-based app
@main
struct TextEditorApp: App {
var body: some Scene {
DocumentGroup(newDocument: TextDocument()) { file in
TextEditorView(document: file.$document)
}
}
}除非你的应用是单一用途的工具,否则请支持多窗口。基于文档的应用必须允许同时打开多个文档。在SwiftUI中使用或。
WindowGroupDocumentGroupswift
// SwiftUI — 基于文档的应用
@main
struct TextEditorApp: App {
var body: some Scene {
DocumentGroup(newDocument: TextDocument()) { file in
TextEditorView(document: file.$document)
}
}
}Rule 2.4 — Title Bar Shows Document Info
规则2.4 — 标题栏显示文档信息
For document-based apps, the title bar must show the document name. Support proxy icon dragging. Show edited state (dot in close button). Support title bar renaming on click.
swift
// AppKit
window.representedURL = document.fileURL
window.title = document.displayName
window.isDocumentEdited = document.hasUnsavedChangesswift
// SwiftUI — NavigationSplitView titles
NavigationSplitView {
SidebarView()
} detail: {
DetailView()
.navigationTitle(document.name)
}对于基于文档的应用,标题栏必须显示文档名称。支持代理图标拖拽。显示编辑状态(关闭按钮中的圆点)。支持点击标题栏重命名。
swift
// AppKit
window.representedURL = document.fileURL
window.title = document.displayName
window.isDocumentEdited = document.hasUnsavedChangesswift
// SwiftUI — NavigationSplitView 标题
NavigationSplitView {
SidebarView()
} detail: {
DetailView()
.navigationTitle(document.name)
}Rule 2.5 — Remember Window State
规则2.5 — 记住窗口状态
Persist window position, size, and state across launches. Use or SwiftUI's built-in state restoration.
NSWindow.setFrameAutosaveNameswift
// AppKit
window.setFrameAutosaveName("MainWindow")
// SwiftUI — Automatic with WindowGroup
WindowGroup(id: "main") {
ContentView()
}
.defaultPosition(.center)在应用重启时保留窗口的位置、大小和状态。使用或SwiftUI的内置状态恢复功能。
NSWindow.setFrameAutosaveNameswift
// AppKit
window.setFrameAutosaveName("MainWindow")
// SwiftUI — 通过WindowGroup自动实现
WindowGroup(id: "main") {
ContentView()
}
.defaultPosition(.center)Rule 2.6 — Traffic Light Buttons
规则2.6 — 交通灯按钮
Never hide or reposition the close (red), minimize (yellow), or zoom (green) buttons. They must remain in the top-left corner. If using a custom title bar, the buttons must still be visible and functional.
swift
// AppKit — Custom title bar that preserves traffic lights
window.titlebarAppearsTransparent = true
window.styleMask.insert(.fullSizeContentView)
// Traffic lights remain functional and visible请勿隐藏或重新定位关闭(红色)、最小化(黄色)或缩放(绿色)按钮。它们必须保持在左上角。如果使用自定义标题栏,按钮仍必须可见且可用。
swift
// AppKit — 保留交通灯的自定义标题栏
window.titlebarAppearsTransparent = true
window.styleMask.insert(.fullSizeContentView)
// 交通灯保持可用且可见3. Toolbars (HIGH)
3. 工具栏(重要要求)
Toolbars are the secondary command surface after the menu bar. They provide quick access to frequent actions and should be customizable.
工具栏是菜单栏之后的次要命令界面。它为常用操作提供快速访问,并且应该支持自定义。
Rule 3.1 — Unified Title Bar and Toolbar
规则3.1 — 统一标题栏和工具栏
Use the unified title bar + toolbar style for a modern appearance. The toolbar sits in the title bar area, saving vertical space.
swift
// SwiftUI
WindowGroup {
ContentView()
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: compose) {
Label("Compose", systemImage: "square.and.pencil")
}
}
}
}
.windowToolbarStyle(.unified)swift
// AppKit
window.titleVisibility = .hidden
window.toolbarStyle = .unified使用统一标题栏+工具栏样式以获得现代外观。工具栏位于标题栏区域,节省垂直空间。
swift
// SwiftUI
WindowGroup {
ContentView()
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: compose) {
Label("Compose", systemImage: "square.and.pencil")
}
}
}
}
.windowToolbarStyle(.unified)swift
// AppKit
window.titleVisibility = .hidden
window.toolbarStyle = .unifiedRule 3.2 — User-Customizable Toolbars
规则3.2 — 用户可自定义的工具栏
Allow users to add, remove, and rearrange toolbar items. Provide a default set and a superset of available items.
swift
// SwiftUI — Customizable toolbar
.toolbar(id: "main") {
ToolbarItem(id: "compose", placement: .primaryAction) {
Button(action: compose) {
Label("Compose", systemImage: "square.and.pencil")
}
}
ToolbarItem(id: "filter", placement: .secondaryAction) {
Button(action: toggleFilter) {
Label("Filter", systemImage: "line.3.horizontal.decrease")
}
}
}
.toolbarRole(.editor)允许用户添加、移除和重新排列工具栏项。提供默认项集和可用项的超集。
swift
// SwiftUI — 可自定义工具栏
.toolbar(id: "main") {
ToolbarItem(id: "compose", placement: .primaryAction) {
Button(action: compose) {
Label("Compose", systemImage: "square.and.pencil")
}
}
ToolbarItem(id: "filter", placement: .secondaryAction) {
Button(action: toggleFilter) {
Label("Filter", systemImage: "line.3.horizontal.decrease")
}
}
}
.toolbarRole(.editor)Rule 3.3 — Segmented Controls for View Switching
规则3.3 — 使用分段控件切换视图
Use a segmented control or picker in the toolbar for switching between content views (e.g., List/Grid/Column). This is a toolbar pattern, not a tab bar.
swift
// SwiftUI
ToolbarItem(placement: .principal) {
Picker("View Mode", selection: $viewMode) {
Label("List", systemImage: "list.bullet").tag(ViewMode.list)
Label("Grid", systemImage: "square.grid.2x2").tag(ViewMode.grid)
Label("Column", systemImage: "rectangle.split.3x1").tag(ViewMode.column)
}
.pickerStyle(.segmented)
}在工具栏中使用分段控件或选择器来切换内容视图(例如,列表/网格/列)。这是工具栏模式,而非标签栏。
swift
// SwiftUI
ToolbarItem(placement: .principal) {
Picker("View Mode", selection: $viewMode) {
Label("List", systemImage: "list.bullet").tag(ViewMode.list)
Label("Grid", systemImage: "square.grid.2x2").tag(ViewMode.grid)
Label("Column", systemImage: "rectangle.split.3x1").tag(ViewMode.column)
}
.pickerStyle(.segmented)
}Rule 3.4 — Search Field in Toolbar
规则3.4 — 工具栏中的搜索字段
Place the search field in the trailing area of the toolbar. Use in SwiftUI for standard search behavior with suggestions and tokens.
.searchable()swift
// SwiftUI
NavigationSplitView {
SidebarView()
} detail: {
ContentListView()
.searchable(text: $searchText, placement: .toolbar, prompt: "Search items")
.searchSuggestions {
ForEach(suggestions) { suggestion in
Text(suggestion.title).searchCompletion(suggestion.title)
}
}
}将搜索字段放置在工具栏的尾部区域。在SwiftUI中使用以获得带有建议和标记的标准搜索行为。
.searchable()swift
// SwiftUI
NavigationSplitView {
SidebarView()
} detail: {
ContentListView()
.searchable(text: $searchText, placement: .toolbar, prompt: "Search items")
.searchSuggestions {
ForEach(suggestions) { suggestion in
Text(suggestion.title).searchCompletion(suggestion.title)
}
}
}Rule 3.5 — Toolbar Labels and Icons
规则3.5 — 工具栏标签和图标
Toolbar items should have both an icon (SF Symbol) and a text label. In compact mode, show icons only. Prefer labeled icons for discoverability. Use to supply both.
Label工具栏项应同时包含图标(SF Symbol)和文本标签。在紧凑模式下,仅显示图标。为了便于发现,优先使用带标签的图标。使用来同时提供两者。
Label4. Sidebars (HIGH)
4. 侧边栏(重要要求)
Sidebars are the primary navigation surface for Mac apps. They appear on the leading edge and provide persistent access to top-level sections and content libraries.
侧边栏是Mac应用的主要导航界面。它位于左侧边缘,提供对顶级区域和内容库的持久访问。
Rule 4.1 — Leading Edge, Collapsible
规则4.1 — 左侧边缘,可折叠
Place the sidebar on the left (leading) edge. Make it collapsible via the toolbar button or Cmd+Ctrl+S. Persist collapsed state.
swift
// SwiftUI
NavigationSplitView(columnVisibility: $columnVisibility) {
List(selection: $selection) {
Section("Library") {
Label("All Items", systemImage: "tray.full")
Label("Favorites", systemImage: "star")
Label("Recent", systemImage: "clock")
}
Section("Tags") {
ForEach(tags) { tag in
Label(tag.name, systemImage: "tag")
}
}
}
.navigationSplitViewColumnWidth(min: 180, ideal: 220, max: 320)
} detail: {
DetailView(selection: selection)
}
.navigationSplitViewStyle(.prominentDetail)将侧边栏放置在左侧(前导)边缘。允许通过工具栏按钮或Cmd+Ctrl+S折叠。保留折叠状态。
swift
// SwiftUI
NavigationSplitView(columnVisibility: $columnVisibility) {
List(selection: $selection) {
Section("Library") {
Label("All Items", systemImage: "tray.full")
Label("Favorites", systemImage: "star")
Label("Recent", systemImage: "clock")
}
Section("Tags") {
ForEach(tags) { tag in
Label(tag.name, systemImage: "tag")
}
}
}
.navigationSplitViewColumnWidth(min: 180, ideal: 220, max: 320)
} detail: {
DetailView(selection: selection)
}
.navigationSplitViewStyle(.prominentDetail)Rule 4.2 — Source List Style
规则4.2 — 源列表样式
Use the source list style () for content-library navigation. Source lists have a translucent background that shows the desktop or window behind them with vibrancy effects.
.listStyle(.sidebar)swift
// SwiftUI
List(selection: $selection) {
ForEach(sections) { section in
Section(section.name) {
ForEach(section.items) { item in
NavigationLink(value: item) {
Label(item.name, systemImage: item.icon)
}
}
}
}
}
.listStyle(.sidebar)使用源列表样式()进行内容库导航。源列表具有半透明背景,可通过活力效果显示背后的桌面或窗口。
.listStyle(.sidebar)swift
// SwiftUI
List(selection: $selection) {
ForEach(sections) { section in
Section(section.name) {
ForEach(section.items) { item in
NavigationLink(value: item) {
Label(item.name, systemImage: item.icon)
}
}
}
}
}
.listStyle(.sidebar)Rule 4.3 — Outline Views for Hierarchies
规则4.3 — 用于层级结构的大纲视图
When content is hierarchical (e.g., folder trees, project structures), use disclosure groups or outline views to let users expand and collapse levels.
swift
// SwiftUI — Recursive outline
List(selection: $selection) {
OutlineGroup(rootNodes, children: \.children) { node in
Label(node.name, systemImage: node.icon)
}
}当内容具有层级结构(例如,文件夹树、项目结构)时,使用展开组或大纲视图让用户展开和折叠层级。
swift
// SwiftUI — 递归大纲
List(selection: $selection) {
OutlineGroup(rootNodes, children: \.children) { node in
Label(node.name, systemImage: node.icon)
}
}Rule 4.4 — Drag to Reorder
规则4.4 — 拖拽重排
Sidebar items that can be reordered (bookmarks, favorites, custom sections) must support drag-to-reorder. Implement or drag delegates.
onMoveNSOutlineViewswift
// SwiftUI
ForEach(favorites) { item in
Label(item.name, systemImage: item.icon)
}
.onMove { source, destination in
favorites.move(fromOffsets: source, toOffset: destination)
}可重排的侧边栏项(书签、收藏、自定义区域)必须支持拖拽重排。实现或拖拽委托。
onMoveNSOutlineViewswift
// SwiftUI
ForEach(favorites) { item in
Label(item.name, systemImage: item.icon)
}
.onMove { source, destination in
favorites.move(fromOffsets: source, toOffset: destination)
}Rule 4.5 — Badge Counts
规则4.5 — 徽章计数
Show badge counts on sidebar items for unread counts, pending items, or notifications. Use the modifier.
.badge()swift
// SwiftUI
Label("Inbox", systemImage: "tray")
.badge(unreadCount)在侧边栏项上显示未读计数、待处理项或通知的徽章。使用修饰符。
.badge()swift
// SwiftUI
Label("Inbox", systemImage: "tray")
.badge(unreadCount)5. Keyboard (CRITICAL)
5. 键盘(关键要求)
Mac users rely on keyboard shortcuts more than any other platform. An app without comprehensive keyboard support is a broken Mac app.
Mac用户比其他平台的用户更依赖键盘快捷键。没有全面键盘支持的应用在Mac上是不合格的。
Rule 5.1 — Cmd Shortcuts for Everything
规则5.1 — 所有操作都使用Cmd快捷键
Every action reachable by mouse must have a keyboard equivalent. Primary actions use Cmd+letter. Secondary actions use Cmd+Shift or Cmd+Option. Tertiary actions use Cmd+Ctrl.
Keyboard Shortcut Conventions:
| Modifier Pattern | Usage |
|---|---|
| Cmd+letter | Primary actions (New, Open, Save, etc.) |
| Cmd+Shift+letter | Variant of primary (Save As, Find Previous) |
| Cmd+Option+letter | Alternative mode (Paste and Match Style) |
| Cmd+Ctrl+letter | Window/view controls (Fullscreen, Sidebar) |
| Ctrl+letter | Emacs-style text navigation (acceptable) |
| Fn+key | System functions (F11 Show Desktop, etc.) |
每个可通过鼠标执行的操作都必须有键盘等效操作。主要操作使用Cmd+字母组合。次要操作使用Cmd+Shift或Cmd+Option组合。三级操作使用Cmd+Ctrl组合。
键盘快捷键约定:
| 修饰符模式 | 用途 |
|---|---|
| Cmd+letter | 主要操作(新建、打开、保存等) |
| Cmd+Shift+letter | 主要操作的变体(另存为、查找上一个) |
| Cmd+Option+letter | 替代模式(粘贴并匹配样式) |
| Cmd+Ctrl+letter | 窗口/视图控制(全屏、侧边栏) |
| Ctrl+letter | Emacs风格的文本导航(可接受) |
| Fn+key | 系统功能(F11 显示桌面等) |
Rule 5.2 — Full Keyboard Navigation
规则5.2 — 全键盘导航
Support Tab to move between controls. Support arrow keys within lists, grids, and tables. Support Shift+Tab for reverse navigation. Use and in SwiftUI.
focusable()@FocusStateswift
// SwiftUI — Focus management
struct ContentView: View {
@FocusState private var focusedField: Field?
var body: some View {
VStack {
TextField("Name", text: $name)
.focused($focusedField, equals: .name)
TextField("Email", text: $email)
.focused($focusedField, equals: .email)
}
.onSubmit { advanceFocus() }
}
}支持使用Tab键在控件之间移动。支持在列表、网格和表格中使用箭头键。支持Shift+Tab反向导航。在SwiftUI中使用和。
focusable()@FocusStateswift
// SwiftUI — 焦点管理
struct ContentView: View {
@FocusState private var focusedField: Field?
var body: some View {
VStack {
TextField("Name", text: $name)
.focused($focusedField, equals: .name)
TextField("Email", text: $email)
.focused($focusedField, equals: .email)
}
.onSubmit { advanceFocus() }
}
}Rule 5.3 — Escape to Cancel or Close
规则5.3 — 使用Escape取消或关闭
Esc must dismiss popovers, sheets, dialogs, and cancel in-progress operations. In text fields, Esc reverts to the previous value. In modal dialogs, Esc is equivalent to clicking Cancel.
swift
// SwiftUI — Sheet with Esc support (automatic)
.sheet(isPresented: $showingSheet) {
SheetView() // Esc dismisses automatically
}
// AppKit — Custom responder
override func cancelOperation(_ sender: Any?) {
dismiss(nil)
}Esc键必须能够关闭弹出框、表单页、对话框,并取消正在进行的操作。在文本字段中,Esc键恢复为之前的值。在模态对话框中,Esc键等效于点击取消按钮。
swift
// SwiftUI — 支持Esc的表单页(自动实现)
.sheet(isPresented: $showingSheet) {
SheetView() // Esc键可自动关闭
}
// AppKit — 自定义响应者
override func cancelOperation(_ sender: Any?) {
dismiss(nil)
}Rule 5.4 — Return for Default Action
规则5.4 — 使用Return执行默认操作
In dialogs and forms, Return/Enter activates the default button (visually emphasized in blue). The default button is always the safest primary action.
swift
// SwiftUI
Button("Save") { save() }
.keyboardShortcut(.defaultAction) // Enter key
Button("Cancel") { cancel() }
.keyboardShortcut(.cancelAction) // Esc key在对话框和表单中,Return/Enter键激活默认按钮(视觉上以蓝色突出显示)。默认按钮始终是最安全的主要操作。
swift
// SwiftUI
Button("Save") { save() }
.keyboardShortcut(.defaultAction) // Enter键
Button("Cancel") { cancel() }
.keyboardShortcut(.cancelAction) // Esc键Rule 5.5 — Delete for Removal
规则5.5 — 使用Delete删除
The Delete key (Backspace) must remove selected items in lists, tables, and collections. Cmd+Delete for more destructive removal (move to Trash). Always support Cmd+Z to undo deletion.
Delete键(退格键)必须能够删除列表、表格和集合中选中的项。Cmd+Delete用于更具破坏性的删除(移至废纸篓)。始终支持Cmd+Z撤销删除操作。
Rule 5.6 — Space for Quick Look
规则5.6 — 使用Space快速查看
When items support previewing, Space bar should invoke Quick Look. Use the API in AppKit or in SwiftUI.
QLPreviewPanel.quickLookPreview()swift
// SwiftUI
List(selection: $selection) {
ForEach(files) { file in
FileRow(file: file)
}
}
.quickLookPreview($quickLookItem, in: files)当项支持预览时,空格键应调用快速查看。在AppKit中使用 API,或在SwiftUI中使用。
QLPreviewPanel.quickLookPreview()swift
// SwiftUI
List(selection: $selection) {
ForEach(files) { file in
FileRow(file: file)
}
}
.quickLookPreview($quickLookItem, in: files)Rule 5.7 — Arrow Key Navigation
规则5.7 — 箭头键导航
In lists and grids, Up/Down arrow keys move selection. Left/Right collapse/expand disclosure groups or navigate columns. Cmd+Up goes to the beginning, Cmd+Down goes to the end.
在列表和网格中,向上/向下箭头键移动选择。向左/向右箭头键折叠/展开展开组或导航列。Cmd+Up键跳至开头,Cmd+Down键跳至结尾。
6. Pointer and Mouse (HIGH)
6. 指针和鼠标(重要要求)
Mac is a pointer-driven platform. Every interactive element must respond to hover, click, right-click, and drag.
Mac是一个基于指针的平台。每个交互元素都必须响应悬停、点击、右键点击和拖拽。
Rule 6.1 — Hover States
规则6.1 — 悬停状态
All interactive elements must have a visible hover state. Buttons highlight, rows show a selection indicator, links change cursor. Use in SwiftUI.
.onHoverswift
// SwiftUI — Hover effect
struct HoverableRow: View {
@State private var isHovered = false
var body: some View {
HStack {
Text(item.name)
Spacer()
if isHovered {
Button("Edit") { edit() }
.buttonStyle(.borderless)
}
}
.padding(8)
.background(isHovered ? Color.primary.opacity(0.05) : .clear)
.cornerRadius(6)
.onHover { hovering in isHovered = hovering }
}
}所有交互元素必须有可见的悬停状态。按钮高亮显示,行显示选择指示器,链接更改光标。在SwiftUI中使用。
.onHoverswift
// SwiftUI — 悬停效果
struct HoverableRow: View {
@State private var isHovered = false
var body: some View {
HStack {
Text(item.name)
Spacer()
if isHovered {
Button("Edit") { edit() }
.buttonStyle(.borderless)
}
}
.padding(8)
.background(isHovered ? Color.primary.opacity(0.05) : .clear)
.cornerRadius(6)
.onHover { hovering in isHovered = hovering }
}
}Rule 6.2 — Right-Click Context Menus
规则6.2 — 右键上下文菜单
Every interactive element must respond to right-click with a contextual menu. The context menu should contain the most relevant actions for the clicked item.
每个交互元素必须响应右键点击并显示上下文菜单。上下文菜单应包含与点击项最相关的操作。
Rule 6.3 — Drag and Drop
规则6.3 — 拖拽和放置
Support drag and drop for content manipulation: reordering items, moving between containers, importing files from Finder, and exporting content.
swift
// SwiftUI — Drag and drop
ForEach(items) { item in
ItemView(item: item)
.draggable(item)
}
.dropDestination(for: Item.self) { items, location in
handleDrop(items, at: location)
return true
}swift
// Accepting file drops from Finder
.dropDestination(for: URL.self) { urls, location in
importFiles(urls)
return true
}支持拖拽和放置以进行内容操作:重排项、在容器之间移动、从Finder导入文件以及导出内容。
swift
// SwiftUI — 拖拽和放置
ForEach(items) { item in
ItemView(item: item)
.draggable(item)
}
.dropDestination(for: Item.self) { items, location in
handleDrop(items, at: location)
return true
}swift
// 接受来自Finder的文件拖放
.dropDestination(for: URL.self) { urls, location in
importFiles(urls)
return true
}Rule 6.4 — Scroll Behavior
规则6.4 — 滚动行为
Support both trackpad (smooth/inertial) and mouse wheel (discrete) scrolling. Use elastic/bounce scrolling at content boundaries. Support horizontal scrolling where appropriate.
同时支持触控板(平滑/惯性)和鼠标滚轮(离散)滚动。在内容边界使用弹性/弹跳滚动。在适当的地方支持水平滚动。
Rule 6.5 — Cursor Changes
规则6.5 — 光标变化
Change the cursor to indicate affordances: pointer for clickable elements, I-beam for text, crosshair for drawing, resize handles at window/splitter edges, grab hand for draggable content.
swift
// AppKit — Custom cursor
override func resetCursorRects() {
addCursorRect(bounds, cursor: .crosshair)
}更改光标以指示功能:可点击元素使用指针光标,文本区域使用I型光标,绘图使用十字光标,窗口/拆分器边缘使用调整大小手柄,可拖拽内容使用抓取手光标。
swift
// AppKit — 自定义光标
override func resetCursorRects() {
addCursorRect(bounds, cursor: .crosshair)
}Rule 6.6 — Multi-Selection
规则6.6 — 多选
Support Cmd+Click for non-contiguous selection and Shift+Click for range selection in lists, tables, and grids. This is a deeply ingrained Mac interaction pattern.
swift
// SwiftUI — Tables with multi-selection
Table(items, selection: $selectedItems) {
TableColumn("Name", value: \.name)
TableColumn("Date", value: \.dateFormatted)
TableColumn("Size", value: \.sizeFormatted)
}在列表、表格和网格中支持Cmd+Click非连续选择和Shift+Click范围选择。这是Mac中根深蒂固的交互模式。
swift
// SwiftUI — 支持多选的表格
Table(items, selection: $selectedItems) {
TableColumn("Name", value: \.name)
TableColumn("Date", value: \.dateFormatted)
TableColumn("Size", value: \.sizeFormatted)
}7. Notifications and Alerts (MEDIUM)
7. 通知和提醒(中等要求)
Mac users are protective of their attention. Only interrupt when truly necessary.
Mac用户非常珍惜自己的注意力。仅在真正必要时才打断用户。
Rule 7.1 — Use Notification Center Appropriately
规则7.1 — 合理使用通知中心
Send notifications only for events that happen outside the app or require user action. Never notify for routine operations. Notifications must be actionable.
swift
// UserNotifications
let content = UNMutableNotificationContent()
content.title = "Download Complete"
content.body = "project-assets.zip is ready"
content.categoryIdentifier = "DOWNLOAD"
content.sound = .default
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)仅在应用外发生事件或需要用户操作时发送通知。不要为常规操作发送通知。通知必须是可操作的。
swift
// UserNotifications
let content = UNMutableNotificationContent()
content.title = "Download Complete"
content.body = "project-assets.zip is ready"
content.categoryIdentifier = "DOWNLOAD"
content.sound = .default
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)Rule 7.2 — Alerts with Suppression Option
规则7.2 — 带有抑制选项的提醒
For recurring alerts, provide a "Do not show this again" checkbox. Respect the user's choice and persist it.
swift
// AppKit — Alert with suppression
let alert = NSAlert()
alert.messageText = "Remove from library?"
alert.informativeText = "The file will be moved to the Trash."
alert.alertStyle = .warning
alert.addButton(withTitle: "Remove")
alert.addButton(withTitle: "Cancel")
alert.showsSuppressionButton = true
alert.suppressionButton?.title = "Do not ask again"
let response = alert.runModal()
if alert.suppressionButton?.state == .on {
UserDefaults.standard.set(true, forKey: "suppressRemoveAlert")
}对于重复出现的提醒,提供“不再显示此提醒”复选框。尊重用户的选择并保留该设置。
swift
// AppKit — 带有抑制选项的提醒
let alert = NSAlert()
alert.messageText = "Remove from library?"
alert.informativeText = "The file will be moved to the Trash."
alert.alertStyle = .warning
alert.addButton(withTitle: "Remove")
alert.addButton(withTitle: "Cancel")
alert.showsSuppressionButton = true
alert.suppressionButton?.title = "Do not ask again"
let response = alert.runModal()
if alert.suppressionButton?.state == .on {
UserDefaults.standard.set(true, forKey: "suppressRemoveAlert")
}Rule 7.3 — Don't Interrupt Unnecessarily
规则7.3 — 不要不必要地打断用户
Never show alerts for successful operations. Use inline status indicators, toolbar badges, or subtle animations instead. Reserve modal alerts for destructive or irreversible actions.
不要为成功操作显示提醒。改用内联状态指示器、工具栏徽章或微妙的动画。仅在执行破坏性或不可逆操作时使用模态提醒。
Rule 7.4 — Dock Badge
规则7.4 — Dock徽章
Show a badge on the Dock icon for notification counts. Clear it promptly when the user addresses the notifications.
swift
// AppKit
NSApp.dockTile.badgeLabel = unreadCount > 0 ? "\(unreadCount)" : nil在Dock图标上显示通知计数的徽章。当用户处理完通知后立即清除徽章。
swift
// AppKit
NSApp.dockTile.badgeLabel = unreadCount > 0 ? "\(unreadCount)" : nil8. System Integration (MEDIUM)
8. 系统集成(中等要求)
Mac apps exist in a rich ecosystem. Deep integration makes an app feel native.
Mac应用存在于一个丰富的生态系统中。深度集成让应用感觉更原生。
Rule 8.1 — Dock Icon and Menus
规则8.1 — Dock图标和菜单
Provide a high-quality 1024x1024 app icon. Support Dock right-click menus for quick actions. Show recent documents in the Dock menu.
swift
// AppKit — Dock menu
override func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
let menu = NSMenu()
menu.addItem(withTitle: "New Window", action: #selector(newWindow(_:)), keyEquivalent: "")
menu.addItem(withTitle: "New Document", action: #selector(newDocument(_:)), keyEquivalent: "")
menu.addItem(.separator())
for doc in recentDocuments.prefix(5) {
menu.addItem(withTitle: doc.name, action: #selector(openRecent(_:)), keyEquivalent: "")
}
return menu
}提供高质量的1024x1024应用图标。支持Dock右键菜单以执行快速操作。在Dock菜单中显示最近的文档。
swift
// AppKit — Dock菜单
override func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
let menu = NSMenu()
menu.addItem(withTitle: "New Window", action: #selector(newWindow(_:)), keyEquivalent: "")
menu.addItem(withTitle: "New Document", action: #selector(newDocument(_:)), keyEquivalent: "")
menu.addItem(.separator())
for doc in recentDocuments.prefix(5) {
menu.addItem(withTitle: doc.name, action: #selector(openRecent(_:)), keyEquivalent: "")
}
return menu
}Rule 8.2 — Spotlight Integration
规则8.2 — Spotlight集成
Index app content for Spotlight search using and Core Spotlight. Users expect to find app content via Cmd+Space.
CSSearchableItemswift
import CoreSpotlight
let attributeSet = CSSearchableItemAttributeSet(contentType: .text)
attributeSet.title = document.title
attributeSet.contentDescription = document.summary
attributeSet.thumbnailData = document.thumbnail?.pngData()
let item = CSSearchableItem(uniqueIdentifier: document.id, domainIdentifier: "documents", attributeSet: attributeSet)
CSSearchableIndex.default().indexSearchableItems([item])使用和Core Spotlight为Spotlight搜索索引应用内容。用户期望通过Cmd+Space找到应用内容。
CSSearchableItemswift
import CoreSpotlight
let attributeSet = CSSearchableItemAttributeSet(contentType: .text)
attributeSet.title = document.title
attributeSet.contentDescription = document.summary
attributeSet.thumbnailData = document.thumbnail?.pngData()
let item = CSSearchableItem(uniqueIdentifier: document.id, domainIdentifier: "documents", attributeSet: attributeSet)
CSSearchableIndex.default().indexSearchableItems([item])Rule 8.3 — Quick Look Support
规则8.3 — 快速查看支持
Provide Quick Look previews for custom file types via a Quick Look Preview Extension. Users expect Space to preview any file in Finder.
通过快速查看预览扩展为自定义文件类型提供快速查看预览。用户期望在Finder中使用空格键预览任何文件。
Rule 8.4 — Share Extensions
规则8.4 — 分享扩展
Implement the Share menu so users can share content from your app to Messages, Mail, Notes, etc. Also accept shared content from other apps.
swift
// SwiftUI
ShareLink(item: document.url) {
Label("Share", systemImage: "square.and.arrow.up")
}实现分享菜单,让用户可以将应用中的内容分享到信息、邮件、备忘录等应用。同时接受来自其他应用的共享内容。
swift
// SwiftUI
ShareLink(item: document.url) {
Label("Share", systemImage: "square.and.arrow.up")
}Rule 8.5 — Services Menu
规则8.5 — 服务菜单
Register for the Services menu to receive text, URLs, or files from other apps. This is a uniquely Mac integration point that power users rely on.
注册服务菜单以接收来自其他应用的文本、URL或文件。这是高级用户依赖的独特Mac集成点。
Rule 8.6 — Shortcuts and AppleScript
规则8.6 — 快捷指令和AppleScript
Support the Shortcuts app by providing App Intents. For advanced automation, add AppleScript/JXA scripting support via an scripting dictionary.
.sdefswift
// App Intents for Shortcuts
struct CreateDocumentIntent: AppIntent {
static var title: LocalizedStringResource = "Create Document"
static var description = IntentDescription("Creates a new document with the given title.")
@Parameter(title: "Title")
var title: String
func perform() async throws -> some IntentResult {
let doc = DocumentManager.shared.create(title: title)
return .result(value: doc.title)
}
}通过提供App Intents支持快捷指令应用。对于高级自动化,通过脚本字典添加AppleScript/JXA脚本支持。
.sdefswift
// App Intents for Shortcuts
struct CreateDocumentIntent: AppIntent {
static var title: LocalizedStringResource = "Create Document"
static var description = IntentDescription("Creates a new document with the given title.")
@Parameter(title: "Title")
var title: String
func perform() async throws -> some IntentResult {
let doc = DocumentManager.shared.create(title: title)
return .result(value: doc.title)
}
}9. Visual Design (HIGH)
9. 视觉设计(重要要求)
Mac apps should look and feel like they belong on the platform. Use system-provided materials, fonts, and colors.
Mac应用应该看起来和感觉上属于该平台。使用系统提供的材质、字体和颜色。
Rule 9.1 — Use System Fonts
规则9.1 — 使用系统字体
Use SF Pro (the system font) at standard dynamic type sizes. Use SF Mono for code. Never hardcode font sizes; use semantic styles.
swift
// SwiftUI — Semantic font styles
Text("Title").font(.title)
Text("Headline").font(.headline)
Text("Body text").font(.body)
Text("Caption").font(.caption)
Text("let x = 42").font(.system(.body, design: .monospaced))使用SF Pro(系统字体)并设置标准动态类型大小。代码使用SF Mono。不要硬编码字体大小;使用语义样式。
swift
// SwiftUI — 语义字体样式
Text("Title").font(.title)
Text("Headline").font(.headline)
Text("Body text").font(.body)
Text("Caption").font(.caption)
Text("let x = 42").font(.system(.body, design: .monospaced))Rule 9.2 — Vibrancy and Materials
规则9.2 — 活力效果和材质
Use system materials for sidebar and toolbar backgrounds. Vibrancy lets the desktop or underlying content show through, anchoring the app to the Mac visual language.
swift
// SwiftUI
List { ... }
.listStyle(.sidebar) // Automatic vibrancy
// Custom vibrancy
ZStack {
VisualEffectView(material: .sidebar, blendingMode: .behindWindow)
Text("Sidebar Content")
}swift
// AppKit — Visual effect view
let visualEffect = NSVisualEffectView()
visualEffect.material = .sidebar
visualEffect.blendingMode = .behindWindow
visualEffect.state = .followsWindowActiveState为侧边栏和工具栏背景使用系统材质。活力效果让桌面或底层内容能够透显出来,将应用与Mac视觉语言紧密结合。
swift
// SwiftUI
List { ... }
.listStyle(.sidebar) // 自动应用活力效果
// 自定义活力效果
ZStack {
VisualEffectView(material: .sidebar, blendingMode: .behindWindow)
Text("Sidebar Content")
}swift
// AppKit — 视觉效果视图
let visualEffect = NSVisualEffectView()
visualEffect.material = .sidebar
visualEffect.blendingMode = .behindWindow
visualEffect.state = .followsWindowActiveStateRule 9.3 — Respect System Accent Color
规则9.3 — 尊重系统强调色
Use the system accent color for selection, emphasis, and interactive elements. Never override it with a fixed brand color for standard controls. Use or only on custom views when appropriate.
.accentColor.tintswift
// SwiftUI — Follows system accent automatically
Button("Action") { doSomething() }
.buttonStyle(.borderedProminent) // Uses system accent color
Toggle("Enable feature", isOn: $isEnabled) // Toggle tint follows accent使用系统强调色进行选择、强调和交互元素。不要为标准控件使用固定品牌颜色覆盖它。仅在合适时对自定义视图使用或。
.accentColor.tintswift
// SwiftUI — 自动遵循系统强调色
Button("Action") { doSomething() }
.buttonStyle(.borderedProminent) // 使用系统强调色
Toggle("Enable feature", isOn: $isEnabled) // 开关色调遵循强调色Rule 9.4 — Support Dark Mode
规则9.4 — 支持深色模式
Every view must support both Light and Dark appearances. Use semantic colors (, , ) rather than hardcoded colors. Test in both modes.
Color.primaryColor.secondary.backgroundswift
// SwiftUI — Semantic colors
Text("Title").foregroundStyle(.primary)
Text("Subtitle").foregroundStyle(.secondary)
RoundedRectangle(cornerRadius: 8)
.fill(Color(nsColor: .controlBackgroundColor))
// Asset catalog: define colors for Both Appearances
// Never use Color.white or Color.black for UI surfaces每个视图必须同时支持浅色和深色外观。使用语义颜色(、、)而非硬编码颜色。在两种模式下进行测试。
Color.primaryColor.secondary.backgroundswift
// SwiftUI — 语义颜色
Text("Title").foregroundStyle(.primary)
Text("Subtitle").foregroundStyle(.secondary)
RoundedRectangle(cornerRadius: 8)
.fill(Color(nsColor: .controlBackgroundColor))
// 资源目录:为两种外观定义颜色
// 不要为UI表面使用Color.white或Color.blackRule 9.5 — Translucency
规则9.5 — 半透明
Respect the "Reduce transparency" accessibility setting. When transparency is reduced, replace translucent materials with solid backgrounds.
swift
// SwiftUI
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
var body: some View {
if reduceTransparency {
Color(nsColor: .windowBackgroundColor)
} else {
VisualEffectView(material: .sidebar, blendingMode: .behindWindow)
}
}尊重“降低透明度”辅助功能设置。当透明度降低时,将半透明材质替换为实色背景。
swift
// SwiftUI
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
var body: some View {
if reduceTransparency {
Color(nsColor: .windowBackgroundColor)
} else {
VisualEffectView(material: .sidebar, blendingMode: .behindWindow)
}
}Rule 9.6 — Consistent Spacing and Layout
规则9.6 — 一致的间距和布局
Use 20pt standard margins, 8pt spacing between related controls, 20pt spacing between groups. Align controls to a grid. Use SwiftUI's built-in spacing or AppKit's Auto Layout with system spacing constraints.
使用20pt标准边距,相关控件之间使用8pt间距,组之间使用20pt间距。将控件对齐到网格。使用SwiftUI的内置间距或AppKit的自动布局并设置系统间距约束。
Keyboard Shortcut Quick Reference
键盘快捷键快速参考
Navigation
导航
| Shortcut | Action |
|---|---|
| Cmd+N | New window/document |
| Cmd+O | Open |
| Cmd+W | Close window/tab |
| Cmd+Q | Quit app |
| Cmd+, | Settings/Preferences |
| Cmd+Tab | Switch apps |
| Cmd+` | Switch windows within app |
| Cmd+T | New tab |
| 快捷键 | 操作 |
|---|---|
| Cmd+N | 新建窗口/文档 |
| Cmd+O | 打开 |
| Cmd+W | 关闭窗口/标签页 |
| Cmd+Q | 退出应用 |
| Cmd+, | 偏好设置/设置 |
| Cmd+Tab | 切换应用 |
| Cmd+` | 在应用内切换窗口 |
| Cmd+T | 新建标签页 |
Editing
编辑
| Shortcut | Action |
|---|---|
| Cmd+Z | Undo |
| Cmd+Shift+Z | Redo |
| Cmd+X / C / V | Cut / Copy / Paste |
| Cmd+A | Select All |
| Cmd+D | Duplicate |
| Cmd+F | Find |
| Cmd+G | Find Next |
| Cmd+Shift+G | Find Previous |
| Cmd+E | Use Selection for Find |
| 快捷键 | 操作 |
|---|---|
| Cmd+Z | 撤销 |
| Cmd+Shift+Z | 重做 |
| Cmd+X / C / V | 剪切 / 复制 / 粘贴 |
| Cmd+A | 全选 |
| Cmd+D | 复制 |
| Cmd+F | 查找 |
| Cmd+G | 查找下一个 |
| Cmd+Shift+G | 查找上一个 |
| Cmd+E | 使用所选内容进行查找 |
View
视图
| Shortcut | Action |
|---|---|
| Cmd+Ctrl+F | Toggle fullscreen |
| Cmd+Ctrl+S | Toggle sidebar |
| Cmd+0 | Show/hide toolbar |
| Cmd++ / Cmd+- | Zoom in/out |
| Cmd+0 | Actual size |
| 快捷键 | 操作 |
|---|---|
| Cmd+Ctrl+F | 切换全屏 |
| Cmd+Ctrl+S | 切换侧边栏 |
| Cmd+0 | 显示/隐藏工具栏 |
| Cmd++ / Cmd+- | 放大/缩小 |
| Cmd+0 | 实际大小 |
Evaluation Checklist
评估检查表
Before shipping a Mac app, verify:
在发布Mac应用之前,请验证:
Menu Bar
菜单栏
- App has a complete menu bar with standard menus
- All actions have keyboard shortcuts
- Menu items dynamically update (enable/disable, title changes)
- Context menus on all interactive elements
- App menu has About, Settings, Hide, Quit
- 应用有包含标准菜单的完整菜单栏
- 所有操作都有键盘快捷键
- 菜单项动态更新(启用/禁用、标题更改)
- 所有交互元素都有上下文菜单
- 应用菜单包含关于、设置、隐藏、退出
Windows
窗口
- Windows are freely resizable with sensible minimums
- Fullscreen and Split View work
- Multiple windows supported (if appropriate)
- Window position and size persist across launches
- Traffic light buttons visible and functional
- Document title and edited state shown (if document-based)
- 窗口可自由调整大小并设置合理的最小值
- 全屏和分屏视图正常工作
- 支持多窗口(如适用)
- 窗口位置和大小在重启后保留
- 交通灯按钮可见且可用
- 显示文档标题和编辑状态(如为基于文档的应用)
Toolbars
工具栏
- Toolbar present with common actions
- Toolbar is user-customizable
- Search field available in toolbar
- 工具栏包含常用操作
- 工具栏支持用户自定义
- 工具栏中提供搜索字段
Sidebars
侧边栏
- Sidebar for navigation (if app has multiple sections)
- Sidebar is collapsible
- Source list style with vibrancy
- 提供用于导航的侧边栏(如应用有多个区域)
- 侧边栏可折叠
- 使用带活力效果的源列表样式
Keyboard
键盘
- Full keyboard navigation (Tab, arrows, Enter, Esc)
- Cmd+Z undo for all destructive actions
- Space for Quick Look previews
- Delete key removes selected items
- No keyboard traps (user can always Tab out)
- 支持全键盘导航(Tab、箭头键、Enter、Esc)
- 所有破坏性操作都支持Cmd+Z撤销
- 使用空格键进行快速查看预览
- Delete键可删除选中项
- 没有键盘陷阱(用户始终可以使用Tab键退出)
Pointer
指针
- Hover states on interactive elements
- Right-click context menus everywhere
- Drag and drop for content manipulation
- Cmd+Click for multi-selection
- Appropriate cursor changes
- 交互元素有悬停状态
- 所有地方都支持右键上下文菜单
- 支持内容操作的拖拽和放置
- 支持Cmd+Click多选
- 光标有适当的变化
Notifications
通知
- Notifications only for important events
- Alerts have suppression option for recurring ones
- No modal alerts for routine operations
- 仅为重要事件发送通知
- 重复提醒有抑制选项
- 不为常规操作使用模态提醒
System Integration
系统集成
- High-quality Dock icon
- Content indexed in Spotlight (if applicable)
- Share menu works
- App Intents for Shortcuts
- 高质量Dock图标
- 内容已索引到Spotlight(如适用)
- 分享菜单正常工作
- 为快捷指令提供App Intents
Visual Design
视觉设计
- System fonts at semantic sizes
- Dark Mode fully supported
- System accent color respected
- Translucency respects accessibility setting
- Consistent spacing on 8pt grid
- 使用语义大小的系统字体
- 完全支持深色模式
- 尊重系统强调色
- 半透明遵循辅助功能设置
- 基于8pt网格的一致间距
Anti-Patterns
反模式
Do not do these things in a Mac app:
-
No menu bar — Every Mac app needs a menu bar. Period. A Mac app without menus is like a car without a steering wheel.
-
Hamburger menus — Never use a hamburger menu on Mac. The menu bar exists for this purpose. Hamburger menus signal a lazy iOS port.
-
Tab bars at the bottom — Mac apps use sidebars and toolbars, not iOS-style tab bars. If you need tabs, use actual document tabs in the tab bar (like Safari or Finder).
-
Large touch-sized targets — Mac controls should be compact (22-28pt height). Users have precise pointer input. Giant buttons waste space and look out of place.
-
Floating action buttons — FABs are a Material Design pattern. On Mac, place primary actions in the toolbar, menu bar, or as inline buttons.
-
Sheet for every action — Don't use modal sheets for simple operations. Use popovers, inline editing, or direct manipulation. Sheets should be reserved for multi-step workflows or important decisions.
-
Custom window chrome — Don't replace the standard title bar, traffic lights, or window controls with custom implementations. Users expect these to work consistently across all apps.
-
Ignoring keyboard — If a power user must reach for the mouse to perform common actions, your keyboard support is insufficient.
-
Single-window only — Unless your app is genuinely single-purpose (calculator, timer), support multiple windows. Users expect to Cmd+N for new windows.
-
Fixed window size — Non-resizable windows feel broken on Mac. Users have displays ranging from 13" laptops to 32" externals and expect to use that space.
-
No Cmd+Z undo — Every destructive or modifying action must be undoable. Users build muscle memory around Cmd+Z as their safety net.
-
Notification spam — Mac apps that send excessive notifications get their permissions revoked. Only notify for events that genuinely need attention.
-
Ignoring Dark Mode — A Mac app that looks wrong in Dark Mode appears abandoned. Always test both appearances.
-
Hardcoded colors — Use semantic system colors, not hardcoded hex values. Your colors should adapt to Light/Dark mode and accessibility settings automatically.
-
No drag and drop — Mac is a drag-and-drop platform. If users can see content, they expect to drag it somewhere.
在Mac应用中请勿执行以下操作:
-
无菜单栏 — 每个Mac应用都需要菜单栏。这是必须的。没有菜单的Mac应用就像没有方向盘的汽车。
-
汉堡菜单 — 永远不要在Mac上使用汉堡菜单。菜单栏就是为此存在的。汉堡菜单标志着这是一个偷懒的iOS移植应用。
-
底部标签栏 — Mac应用使用侧边栏和工具栏,而非iOS风格的标签栏。如果需要标签,请使用实际的文档标签栏(如Safari或Finder)。
-
大尺寸触控目标 — Mac控件应该紧凑(22-28pt高度)。用户有精确的指针输入。巨大的按钮浪费空间且显得格格不入。
-
浮动操作按钮 — FAB是Material Design模式。在Mac上,将主要操作放置在工具栏、菜单栏或内联按钮中。
-
每个操作都使用表单页 — 不要为简单操作使用模态表单页。使用弹出框、内联编辑或直接操作。表单页应保留给多步骤工作流或重要决策。
-
自定义窗口边框 — 不要用自定义实现替换标准标题栏、交通灯或窗口控件。用户期望这些控件在所有应用中都能一致工作。
-
忽略键盘 — 如果高级用户必须伸手去拿鼠标才能执行常见操作,那么你的键盘支持是不足的。
-
仅单窗口 — 除非你的应用确实是单一用途的(计算器、计时器),否则请支持多窗口。用户期望使用Cmd+N新建窗口。
-
固定窗口大小 — 不可调整大小的窗口在Mac上感觉是不合格的。用户的显示器从13英寸笔记本到32英寸外接显示器不等,他们期望能够利用这些空间。
-
无Cmd+Z撤销 — 每个破坏性或修改性操作都必须可撤销。用户已经形成了将Cmd+Z作为安全网的肌肉记忆。
-
通知垃圾信息 — 发送过多通知的Mac应用会被用户撤销权限。仅为真正需要关注的事件发送通知。
-
忽略深色模式 — 在深色模式下显示异常的Mac应用看起来像是被遗弃了。始终在两种外观下进行测试。
-
硬编码颜色 — 使用语义系统颜色,而非硬编码的十六进制值。你的颜色应该自动适应浅色/深色模式和辅助功能设置。
-
无拖拽和放置 — Mac是一个拖拽和放置平台。如果用户能看到内容,他们期望能够将其拖拽到其他地方。