Pock 深度指南:在 MacBook Touch Bar 上打造你的 Widgets 管理面板
Pock 深度指南:在 MacBook Touch Bar 上打造你的 Widgets 管理面板
Pock 是一款面向 MacBook Touch Bar 的免费开源「Widgets 管理器」:它把 macOS Dock、正在播放的媒体信息、ESC 键、系统状态与控制中心等常用功能搬进 Touch Bar,帮助你在全屏工作或观影时最大化屏幕可用面积。本文以官方 README.md 为骨架,结合仓库源码逐层拆解 Pock 的安装、使用、内置 Widgets、偏好设置、自定义布局、Widgets 安装与更新机制,并深入到 AppController、WidgetsLoader、WidgetsInstaller、Updater 等核心实现,让普通用户能快速上手、开发者能理解其架构与扩展方式。
一、Pock 是什么
Pock 的核心定位在 README.md 中写得很清楚:
Widgets manager for MacBook's Touch Bar
它是一款免费、开源的工具,让你在「不放任何内容在屏幕上」的情况下,快速触达最常用的控件与服务。默认情况下,系统 Dock 占用了屏幕底部一条宝贵空间,而 Pock 将 Dock 等元素"移植"到 Touch Bar 中,配合"全宽"或"保留 Control Strip"两种布局,让屏幕真正回归内容本身。
从仓库的 Info.plist 可以看到,Pock 本身是一个 LSUIElement 为 true 的菜单栏型(Menu Bar-only)应用:它不占用 Dock 图标,主要通过菜单栏状态项(NSStatusBar)与 Touch Bar 交互,这与 AppDelegate.swift 中 mainBarItem / mainBarMenu 的实现完全对应。
二、安装 Pock
README 给出了最简单的三步安装流程:
- 前往 Pock 官方网站点击下载(
pock_0_9_0__22.zip之类的最新归档包)。 - 双击文件或运行
unzip解压归档。 - 将解压得到的
Pock.app拖入/Applications。
注意:安装与运行 Pock 需要 macOS 10.15+(见 Podfile 的
platform :osx, '10.15'),且必须运行在带 Touch Bar 的 MacBook Pro 上。
三、使用入门
3.1 启动与菜单栏入口
安装完成后打开 Pock,随后:
- 检查菜单栏(Menu Bar):启动后会出现一个新的 Pock 图标(源码中由 AppDelegate.swift 使用
pock-inner-icon模板图标创建)。 - 点击该图标即可看到 Pock 的主菜单,从中进入 Preferences…(偏好设置) 与 Manage Widgets…(管理 Widgets)。
菜单结构在 AppDelegate.swift 中完整定义,包含以下几组:
| 菜单分组 | 菜单项 | 快捷键 |
|---|---|---|
| General | About Pock | – |
| General | Preferences… | , |
| Widgets | Manage widgets… | m |
| Widgets | Install widget… | i |
| Customisation | Customize Pock… | p |
| Customisation | Customize Control Strip… | c |
| Advanced | Re-install default widgets… | w |
| Advanced | Open widgets folder… | f |
| Advanced | Reload Pock… / Relaunch Pock… | r / R |
| Advanced | Reload Control Strip… / Relaunch Touch Bar… | s / S |
| Advanced | Show debug console… | d |
| Goodbye | Quit Pock | q |
如果启动后 Touch Bar 上没有出现 Pock,README 的提示是关键:
前往「系统偏好设置 → 键盘」,将「触控栏显示(Touch Bar shows)」设为 App Controls,然后重新启动 Pock。
这一步骤的本质原因可以从 TouchBarHelper.swift 中看到:Pock 依赖系统 Touch Bar 的 PresentationMode 设置(PresentationModeGlobal / com.apple.touchbar.agent),并通过 CFPreferences 读写系统控制条偏好,因此系统必须先允许 App Controls 呈现方式。
3.2 首次启动引导(On-Board)
首次启动时,Pock 会展示欢迎引导页(OnBoardViewController.swift),提示你"点击 Touch Bar 上的提示安装默认 Widgets"。Preferences<a href="https://link.gitcode.com/i/58160121fe4bfb934355a5dff9aa0283" target="_blank">.didShowOnBoard] 标记会在引导页加载后被置为 true,下次启动不再弹出([AppDelegate.swift)。
四、权限与隐私
4.1 系统权限建议
为了获得"无瑕疵的体验",README 建议阅读 Pock 官方文档中关于 Catalina(macOS 10.15)权限 的说明。在实际使用中,涉及的关键权限包括:
- 辅助功能(Accessibility):Pock 需要观察全局键盘事件(如双击
^Control切换 Touch Bar,见 HotKey.swift 的NSEvent.addGlobalMonitorForEvents)以及截取 Touch Bar 视图。 - 屏幕录制(Screen Recording):某些 Touch Bar 视图抓取(snapshot)场景会触发系统授权提示。
4.2 匿名遥测说明
README 明确声明:Pock 会通过 AppCenter 发送一些匿名事件用于加速问题/Bug 的排查;这些事件绝不会用于营销目的(它不是一个商业软件包)。如果不认可这一点,请勿下载使用。
该声明在源码中得到印证:AppDelegate.swift 在 #if !DEBUG 分支下读取 Secrets.plist 中的 AppCenter 密钥并初始化 Analytics 与 Crashes 服务;各核心控制器(如 PreferencesViewController.swift)中散布着 Analytics.trackEvent(...) 调用。Debug 构建则不启用遥测,且 Roger.swift 的日志级别也会放宽。
五、卸载 Pock
- 退出 Pock(菜单栏图标 → Quit Pock,或快捷键
q)。 - 将
/Applications中的Pock.app移到废纸篓。
如果卸载后 Touch Bar 没有恢复默认的系统控件,请前往「系统偏好设置 → 键盘」,选择 "Touch Bar shows App Controls",或按需配置系统设置。这是因为 Pock 卸载/退出时会通过
tearDownTouchBar()恢复用户自定义的呈现模式(见 AppController.swift 与 AppDelegate.swift)。
六、内置 Widgets 详解
Pock 内置 五个默认 Widgets(Dock、Now Playing、ESC、Status、Control Center),另有 Weather 等更多 Widget 可扩展。源码层面,默认 Widget 通过 installDefaultWidgets() 从官方接口逐个下载安装(见 WidgetsInstaller.swift),并且全部安装在用户目录的 ~/Library/Application Support/Pock/Widgets/ 下(路径常量定义在 WidgetsLoader.swift)。
6.1 Dock widget —— 核心 Widget
你的 macOS Dock 被"搬进"了 Touch Bar,这是 Pock 的核心 Widget。它具备:
- 完整的徽标(Badge)支持:不错过任何未读通知。
- 多窗口支持:可以对同一 App 的多个窗口进行切换/展示。
- 更多特性可在 Widget 设置与 Pock 偏好中配置。
它的价值正如 README 所说——每次都能享受全尺寸屏幕。
6.2 Now Playing widget
在 Touch Bar 上一览媒体信息,并内建媒体控制手势。README 给出了完整的手势表:
| 手势 | 动作 |
|---|---|
| 轻点(tap) | 播放 / 暂停切换 |
| 向左轻扫(swipe left) | 上一曲 |
| 向右轻扫(swipe right) | 下一曲 |
6.3 ESC widget
Touch Bar 上常驻一个 ESC 按钮,当你不想隐藏 Pock 去调出系统 ESC 时,随时可以点按。
6.4 Status widget
时刻关注系统信息:当前时间、WiFi 状态、电池状态等。可在设置中配置要在 Status Widget 中展示哪些条目。
6.5 Control Center widget
在 Touch Bar 上调节屏幕亮度与系统音量。在 Control Center 条目上长按或滑动可唤出滑杆精细调节。
6.6 Weather widget
在 Touch Bar 中直接展示你所在位置的天气与预报,随时了解最新天气动态。仓库中 Special mentions 提到其数据服务使用了 openweather-proxy-rs 方案。
6.7 更多 Widgets
README 预告"更多 Widgets 即将到来"。同时,Pock 提供了一套完整的第三方 Widget 生态(见后文"为 Pock 开发自定义 Widget")。
实用提示:如果 Touch Bar 上一个 Widget 都没有,Pock 会在 Touch Bar 上显示"空状态"引导页(EmptyTouchBarController.swift),提示你 Tap to install default widgets… 或 Tap to add widgets to Pock…,一键进入默认 Widget 安装或自定义面板。
七、偏好设置(Preferences)
Pock 的偏好设置可从菜单栏的 Preferences… 与 Manage Widgets… 进入。底层由 Preferences.swift 的 Preferences 结构体统一读写 UserDefaults(个别项如"登录启动"走 LoginServiceKit)。全部设置键与默认值如下:
| Keys | 含义 | 默认值 |
|---|---|---|
allowBlankTouchBar |
允许空 Touch Bar(移除全部 Widget 后 Touch Bar 可完全关闭) | false |
launchAtLogin |
登录时自动启动 Pock | false(依赖系统登录项) |
layoutStyle |
布局风格:withControlStrip / fullWidth |
withControlStrip |
mouseSupportEnabled |
启用光标(鼠标/触控板)支持 | true |
showTrackingArea |
显示光标追踪区域(调试用途,蓝色高亮) | false |
checkForUpdatesOnceADay |
每天自动检查一次更新 | false |
userDefinedPresentationMode |
默认 Touch Bar 呈现模式 | undefined(首次启动时记录系统当前值) |
didShowOnBoard |
是否已展示首次引导页 | false |
showDebugConsoleOnLaunch |
启动时显示调试控制台 | false |
7.1 常规(General)
- Allow blank Touch Bar:勾选后,你可以通过移除所有 Widget 让 Touch Bar 完全关闭。若不勾选,当 Widget 列表为空时,Pock 会自动显示"空状态"引导页(PockTouchBarController.swift)。
- Launch at login:登录时自动启动。此选项由
LoginServiceKit维护系统登录项(见 Preferences.swift 的 setter 分支)。
7.2 布局(Layout)
Pock 提供两种布局风格,对应 LayoutStyle 枚举:
- Show Control Strip(保留 Control Strip):Pock 的 Touch Bar 与系统 Control Strip(亮度、音量、Siri 等系统控件区)并存。
- Full width(全宽):Pock 独占整个 Touch Bar,最大化 Widget 空间。
两种布局在 Touch Bar 上的实际效果示意如下(资源来自仓库 Layout Styles 目录):
保留 Control Strip 的布局风格示意
全宽布局风格示意
源码层面,PockTouchBarController.present() 会根据布局风格决定 Touch Bar 的 placement 与系统 PresentationMode:
.withControlStrip→placement = 0、presentationMode = .appWithControlStrip.fullWidth→placement = 1、presentationMode = .app
详见 PockTouchBarController.swift。
7.3 切换默认 Touch Bar 与呈现模式
- 双击
^Control键即可在 Pock 与系统默认 Touch Bar 之间切换。这是通过 HotKey.swift 实现的"双击快捷键":全局监控flagsChanged事件,0.6 秒内连续两次按下^Control才触发toggleVisibility。 - Default Touch Bar shows:可设置默认 Touch Bar 的呈现模式,可选项包括 App、App with Control Strip、Full Control Strip、Function Keys (Fn)、Workflows、Spaces 等(完整枚举见 TouchBarHelper.swift 的
PresentationMode)。设置会写入系统偏好com.apple.touchbar.agent,并在 Pock 隐藏时恢复该模式。
温馨提示:偏好设置中会提示,为了正常工作 Pock 可能会覆盖系统默认 Touch Bar 偏好;你可以随时从「系统偏好设置 → 键盘」中重新配置。
7.4 光标选项(Cursor options)
- Enable cursor support:启用后可用光标(触控板/鼠标)操作 Touch Bar 上的按钮与滑杆。Pock 通过
PKScreenEdgeController在 Touch Bar 顶部边缘建立鼠标追踪区域,并将进入/移动/点击/滚动事件转发给各 Widget(PockTouchBarController.swift)。 - Show tracking area:开发调试用,把追踪区域以蓝色高亮显示。
7.5 更新(Updates)
- Check for updates once a day:勾选后每天自动检查一次更新。底层由 AppController.swift 的
prepareOnceADayTimer()维护一个 24 小时周期定时器(首次触发在当天 11:20 后 24 小时)。 - Check for updates:手动立即检查。若发现新版本会弹出升级确认框,确认后打开官方下载链接。
更新检查通过 Updater.swift 请求 https://pock.app/api/latestVersions.json(Debug 构建为 stage.pock.app),返回数据模型为 LatestReleases.swift 中的 core(Pock 本体版本)+ widgets(各 Widget 最新版本字典)。版本对象 Version.swift 包含 name、link、changelog 与可选的 core_min(要求的最低 Pock 版本,不满足时提示先升级 Pock)。
有新版本时,菜单栏的 Preferences… 与 Manage Widgets… 菜单项上会显示红色徽标(badge),提示核心或 Widget 有更新(AppDelegate.swift)。
八、自定义 Touch Bar 布局
从菜单栏选择 Customize Pock… 即可进入 macOS 原生的 Touch Bar 自定义面板(NSTouchBar Customization Palette):
- 在自定义面板中,你可以调整 Widget 在 Touch Bar 中的顺序,并添加/移除 Widget。
- 每个 Widget 在面板中显示为一张"快照"预览图,方便识别。该快照由 PKWidgetTouchBarItem.swift 生成:Widget 若实现
imageForCustomization则直接使用自定义预览图,否则自动截取自身视图(NSButton有特殊处理逻辑)作为预览。
实现链路如下:AppController.openPockCustomizationPalette() → 最小化 Pock 的 Touch Bar → 注册 NSTouchBarDidExitCustomization 通知 → NSApp.toggleTouchBarCustomizationPalette() → 退出自定义后重新呈现 Pock Touch Bar(AppController.swift)。
同理,Customize Control Strip… 会打开系统 Control Strip 的自定义面板(AppController.swift)。
九、Widgets 的安装、更新与卸载实战
9.1 Widget 的存储位置
所有 Widget 以 bundle(.pock) 形式存放在:
~/Library/Application Support/Pock/Widgets/
WidgetsLoader 启动时会扫描该目录(过滤掉 disabled 与带 / 的文件),逐个通过 PKWidgetInfo.swift 解析 Info.plist 元信息并加载(WidgetsLoader.swift)。
9.2 支持的安装格式
Pock 支持两种 Widget 包格式(WidgetsInstallViewController.swift):
| 格式 | 说明 |
|---|---|
.pock |
单个 Widget bundle,直接复制到 Widgets 目录 |
.pkarchive |
Widget 归档包,解压后再安装内部 bundle |
两种格式也都在 Info.plist 中注册为 Pock 的文档类型(CFBundleDocumentTypes),因此双击 .pock / .pkarchive 文件即可直接唤起 Pock 的安装流程(WidgetsInstaller 作为 NSDocument 子类接管处理)。
9.3 安装新 Widget
在菜单栏选择 Install widget…(快捷键 i)后,会出现安装面板:
- 直接将
.pock或.pkarchive文件拖入面板,或点击 Choose… 从文件选择器选取。 - 确认后 Pock 将文件复制到 Widgets 目录并提示重载/重启 Pock 生效。
源码实现中,.pock 走 installWidget(widget:removeSource:),.pkarchive 走 extractAndInstall(name:atLocation:removeSource:)(使用 Zip 框架解压到临时目录再装回正式目录),详见 WidgetsInstaller.swift。
9.4 管理 Widget
打开 Manage widgets…(快捷键 m)进入 Widgets Manager(WidgetsManagerViewController.swift),列表会显示:
- 每个 Widget 的加载状态(已加载/未加载)、名称、作者、版本。
- 选中某个 Widget 后可执行 Update(更新)、Remove(卸载),若 Widget 自带偏好面板,还会在右侧内嵌展示该 Widget 的专属设置(
PKWidgetPreference,含 Reset preferences 重置按钮)。 - 顶部 Check for updates 按钮可手动刷新所有 Widget 的最新版本信息;有新版可用的 Widget 会在列表中显示更新徽标。
进阶:Advanced → Re-install default widgets…(
w)可重新安装全部默认 Widget;Advanced → Open widgets folder…(f)会直接在 Finder 中打开 Widgets 目录(AppDelegate.swift),方便手动管理或排查。
9.5 更新与升级限制
更新 Widget 时,Updater.newVersion(for:) 会做两项检查(Updater.swift):
- 若新版 Widget 声明了
coreMin(最低 Pock 核心版本)且高于当前 Pock 版本,则拒绝更新并提示先升级 Pock。 - 仅当远程版本号高于本地版本时才给出更新入口。
更新面板会展示该版本附带的 Changelog,下载进度以进度条实时反馈(Downloader 基于 URLSessionDownloadTask 计算 bytesWritten / totalBytesExpectedToWrite,见 Downloader.swift)。
十、为 Pock 开发自定义 Widget(PockKit)
Pock 提供了名为 PockKit 的官方开发框架,用于构建自定义 Widget。从源码可以还原一个 .pock Widget bundle 的关键元数据约定(PKWidgetInfo.swift 的 BundleKeys):
| Info.plist 键 | 含义 | 是否必需 |
|---|---|---|
CFBundleIdentifier |
Widget 唯一标识(决定 Touch Bar 项 identifier) | 是 |
CFBundleDisplayName / CFBundleName |
显示名称 | 是 |
CFBundleShortVersionString |
版本号 | 是 |
CFBundleVersion |
构建号(值为 1 时忽略) | 可选 |
PKWidgetAuthor |
作者 | 是 |
PKWidgetPreferenceClass |
偏好设置控制器类名(实现 PKWidgetPreference) |
可选 |
NSPrincipalClass |
Widget 主类(必须是 PKWidget 子类) |
是 |
Widget 主类需要继承 PKWidget,并可选择性实现 viewWillAppear / viewDidAppear / viewWillDisappear / viewDidDisappear、imageForCustomization、prepareForCustomization、PKScreenEdgeMouseDelegate(鼠标支持)等生命周期与能力接口(见 PKWidgetViewController.swift 与 PKWidgetTouchBarItem.swift)。加载时,Pock 通过 bundle.principalClass 实例化 Widget 主类,并将其实例包装为 NSCustomTouchBarItem 插入 Touch Bar。
在 Podfile 中可以看到,Pock 本体通过 pod 'PockKit', :git => 'git@github.com:pock/pockkit.git' 依赖 PockKit;第三方开发者构建自定义 Widget 时则直接以 PockKit 为开发依赖。仓库中还带有 QLPockWidget(Quick Look 插件,PreviewViewController.swift)与 Relaunch(重启辅助程序,main.swift)两个辅助 Target,分别用于快速预览 .pock 包和在更新/安装后自动重启 Pock(AppController.relaunch(),见 AppController.swift)。
十一、参与贡献与依赖
- 贡献:提交 Issue 或 Pull Request 前,请阅读仓库的 CONTRIBUTING.md,遵循其中规定的指南与规范。
- 翻译:README 已提供多语言版本,见 .github/readmes 目录(英文 README.md、中文 README.cn.md、日文 README.ja.md、土耳其文 README.tr.md);部分翻译可能存在差异或不完整。
- PockKit:Pock 的核心 Widget 框架。
- AppCenter / AppCenter-Crashes:匿名遥测与崩溃收集。
- Magnet:全局快捷键支持。
- Zip:
.pkarchive归档解压。 - TinyConstraints:Auto Layout 便捷约束。
十二、总结
Pock 的价值在于让 Touch Bar 从"系统固定的几个按键"变成完全由你掌控的可扩展面板:内置的 Dock、Now Playing、ESC、Status、Control Center 与 Weather Widget 覆盖了高频需求;偏好设置提供了布局、呈现模式、光标支持与更新策略的精细控制;而 PockKit 则向开发者开放了完整的自定义 Widget 生态。通过阅读本仓库源码(入口建议从 AppDelegate.swift → AppController.swift → PockTouchBarController.swift 沿调用链展开),无论是普通用户排查问题,还是开发者构建自己的 Touch Bar Widget,都能获得清晰的工程指引。