关于 watchOS
面向 watchOS 应用开发的指导技能,适用于构建 Watch 应用、扩展和表盘复杂功能。内容涵盖 SwiftUI 界面、设备间数据传输、WidgetKit、HealthKit 与无障碍设计。
主要功能
- 01Watch 优先的界面与导航指导
- 02Watch Connectivity 数据传输
- 03ClockKit 与 WidgetKit 复杂功能
- 04HealthKit 锻炼与后台运行
- 05watchOS 无障碍设计
- 06性能与电池使用建议
适用场景
- 01设计适合快速查看的 Watch 界面
- 02同步 iPhone 与 Watch 应用数据
- 03创建表盘复杂功能和 Smart Stack 小组件
- 04实现锻炼追踪与无障碍交互
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rshankras/ claude-code-apple-skills --skill watchos安装后在 Claude Code 中输入/watchos确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
complications.mdhealth-fitness.mdwatch-connectivity.mdwidgets-for-watch.md详细介绍
watchOS 开发
使用 SwiftUI、Watch Connectivity 和复杂功能进行 watchOS 应用开发的全面指南。
此技能的适用场景
在以下情况下使用此技能:
- 正在构建 watchOS 应用或 Watch 扩展
- 询问 Watch Connectivity(iPhone ↔ Watch 同步)
- 需要复杂功能或 ClockKit 方面的帮助
- 想要实现 watch 专属的 UI 模式
- 询问 WidgetKit 复杂功能,或从 ClockKit 迁移到 WidgetKit
- 想要构建 表盘复杂功能(accessoryCircular、accessoryRectangular、accessoryCorner、accessoryInline)
- 询问 watchOS 上的 HealthKit、锻炼会话、心率或健身追踪
- 需要用于后台锻炼追踪的 扩展运行时会话
- 想要构建 watchOS 小组件或智能叠放小组件
- 询问 小组件相关性、智能叠放排序或小组件建议
- 需要在 iOS 和 watchOS 之间共享跨平台小组件
- 询问 watchOS 辅助功能——VoiceOver、AssistiveTouch 或 Watch 上的动态字体大小
关键原则
1. 以 Watch 为先的设计
- 内容一目了然——用户只看几秒,而非几分钟
- 快速交互——不超过 2 秒
- 只提供必要信息——避免大段可滚动文本
- 触控目标足够大——高度至少为 38pt
2. 独立应用与配套应用
- 尽可能优先选择独立 Watch 应用
- 使用 Watch Connectivity 同步数据,而非将其作为依赖
- 在本地缓存数据,以便离线访问
- 妥善处理连接失败
3. 性能
- 尽量减少后台工作(节省电池)
- 谨慎使用复杂功能更新
- 优先使用基于时间线的内容,而非实时更新
- 保持视图轻量
watchOS 设计规则(WWDC20/23)
十秒测试
围绕大约十秒的注意力时长进行设计:“如果你只有十秒钟的时间吸引某人的注意,会展示哪些信息?”直接进入相应的详情视图——根据位置、最近使用情况或频率选择——并让内容清晰醒目,无需标题也能理解。
三种基础布局
| 布局 | 用途 | 备注 |
|---|---|---|
| 表盘式 | 密集的一目了然状态信息 | 最多 4 个角落控件;使用 .scenePadding(.horizontal) 与表圈对齐 |
| 信息图表式 | 图表 + 指标 | 一张图表搭配辅助数字 |
| 列表式 | 可滚动查找 | 用户需要查找某个项目时使用 |
导航模型
- 优先使用 Digital Crown 进行垂直分页,在有明确用途、单屏高度的页面之间切换——“横向分页更难导航”。
- 优先采用两层的 Source List 模式并搭配
NavigationSplitView:始终初始化选中项,让应用启动后直接进入详情,并且不给源列表添加标题。 - 只有其他方式都不适用时才使用
NavigationStack——层级导航应记住上次离开的目标位置,并在下次启动时恢复。 - Digital Crown 可用于导航、滚动和精确输入,但必须始终同时支持触控。
// Source List: launch to detail, not the list
NavigationSplitView {
List(rooms, selection: $selectedRoom) { room in // source list stays untitled
Text(room.name)
}
} detail: {
RoomView(room: selectedRoom)
}
// Initialize selectedRoom (last used / most relevant) so launch lands on detail
背景与材质
- 背景必须具有实用价值——便于识别或传达信息(例如追踪太阳的渐变,或从黑色变为橙色来表示状态变化),不可仅用于装饰。
- 四种鲜明的全屏材质(从 Ultra Thin 到 Thick)搭配 Primary–Quaternary 鲜明前景样式和鲜明语义颜色,确保内容在任何背景上都清晰易读。
工具栏与操作按钮
- 工具栏位置:
.topBarLeading、.topBarTrailing(会将时间移至中央)和.bottomBar。 - 详情视图底部的操作按钮最容易被发现。红色标签表示具有破坏性的操作——如果数据无法恢复,请添加确认步骤。
- “更多”按钮(圆形容器中的省略号:白色,透明度为 85%,带有透明度为 50%、宽度为 1pt 的黑色外发光)仅用于次要操作,绝不可用于主要操作。
- 仅在可滚动视图中使用工具栏中显示的按钮——滚动本身能让用户发现这些按钮。
watchOS 辅助功能(WWDC21 10223)
Watch 上的动态字体
- watchOS 有 11 种文本样式;固定的
.font(.system(size: 24))不会缩放——请使用.font(.title3)等样式。 - 允许文本换行:
lineLimit(1)会在辅助功能字号下截断文本——请设置你支持的实际最大行数(.lineLimit(3)),或移除行数限制。 - Watch 设置会默认将文本大小设为最接近已配对 iPhone 设置的值——请考虑到真实用户可能使用辅助功能字号(WWDC21 10223)。
- 换行显得拥挤时,请切换布局:
@Environment(\.sizeCategory) var sizeCategory
var body: some View {
if sizeCategory < .extraExtraLarge {
PlantViewHorizontal(plant: $plant) // default layout
} else {
PlantViewVertical(plant: $plant) // stacked layout for large sizes
}
}
VoiceOver
NavigationLink会自动组合其子项的辅助功能信息——不要在其中额外添加分组;整行会成为单个元素(WWDC21 10223)。- 为图标与文本组成的行添加标签,使其朗读为含义,而不是各个部分:使用
.accessibilityLabel("Watering in five days"),而不是“水滴,图像。五天后”。也为纯图标按钮添加标签:.accessibilityLabel("Log \(task.name)")→“记录浇水,按钮”。 - 步进器/计数器:将 [减号、数值、加号] 合并为一个可调整元素。将变化的数字放入值中——每次变化时都会重新朗读;标签只会在导航时朗读:
CustomCounter(value: value, increment: increment, decrement: decrement)
.accessibilityElement() // drops the +/- buttons as separate stops
.accessibilityAdjustableAction { direction in
switch direction {
case .increment: increment() // swipe up
case .decrement: decrement() // swipe down
default: break
}
}
.accessibilityLabel("\(task.name) frequency")
.accessibilityValue("\(value) days")
- Complication 和动态通知也需要采用相同处理方式——它们是应用之外的额外内容入口。展开缩写(使用“星期三,3 月 9 日”,而不是“星期三 3 月 9 日”),并为图像 Complication 添加标签,否则 VoiceOver 会朗读资源名称(WWDC21 10223)。
AssistiveTouch
手势无需触碰屏幕即可操作 Watch:握拳 = 轻点,双击握拳 = 操作菜单,捏合 = 下一个元素,双击捏合 = 上一个元素(WWDC21 10223)。光标只会聚焦可交互元素——Button、Toggle、NavigationLink、带有轻点手势的视图、辅助功能操作或具有可操作特征的元素;静态文本和禁用元素会被跳过。
// ✅ static text whose parent owns the tap gesture — make it a cursor stop
FreeDrinkInfoView()
.accessibilityRespondsToUserInteraction(true)
// ✅ cursor frame == tappable area; enlarge tiny hit targets
NavigationLink(destination: EditView()) {
Image(systemName: "ellipsis").symbolVariant(.circle)
}
.contentShape(Circle().scale(1.5))
VoiceOver 自定义操作会自动显示在 AssistiveTouch 操作菜单中。请使用 .accessibilityAction { } label: { Label("Edit", systemImage: "ellipsis.circle") } 的 Label 形式提供实际图标——否则菜单会回退为操作名称的首字母(WWDC21 10223)。
架构模式
应用结构
@main
struct MyWatchApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
导航
// Use NavigationStack (watchOS 9+)
NavigationStack {
List {
NavigationLink("Item 1", value: Item.one)
NavigationLink("Item 2", value: Item.two)
}
.navigationDestination(for: Item.self) { item in
ItemDetailView(item: item)
}
}
// TabView for main sections
TabView {
HomeView()
ActivityView()
SettingsView()
}
.tabViewStyle(.verticalPage)
列表设计
List {
ForEach(items) { item in
ItemRow(item: item)
}
.onDelete(perform: delete)
}
.listStyle(.carousel) // For focused content
.listStyle(.elliptical) // For browsing
Watch Connectivity
会话设置
import WatchConnectivity
@Observable
final class WatchConnectivityManager: NSObject, WCSessionDelegate {
static let shared = WatchConnectivityManager()
private(set) var isReachable = false
override init() {
super.init()
if WCSession.isSupported() {
WCSession.default.delegate = self
WCSession.default.activate()
}
}
// Required delegate methods
func session(_ session: WCSession, activationDidCompleteWith state: WCSessionActivationState, error: Error?) {
isReachable = session.isReachable
}
#if os(iOS)
func sessionDidBecomeInactive(_ session: WCSession) {}
func sessionDidDeactivate(_ session: WCSession) {
WCSession.default.activate()
}
#endif
}
数据传输方式
| 方式 | 使用场景 | 传递方式 |
|---|---|---|
updateApplicationContext | 最新状态(设置) | 覆盖之前的内容 |
sendMessage | 实时通信,两个应用均处于活跃状态 | 立即传递 |
transferUserInfo | 排队传输的数据 | 按顺序保证传递 |
transferFile | 大型数据 | 后台传输 |
// Application Context (most common)
func updateContext(_ data: [String: Any]) throws {
try WCSession.default.updateApplicationContext(data)
}
// Real-time messaging
func sendMessage(_ message: [String: Any]) {
guard WCSession.default.isReachable else { return }
WCSession.default.sendMessage(message, replyHandler: nil)
}
// Receiving data
func session(_ session: WCSession, didReceiveApplicationContext context: [String: Any]) {
Task { @MainActor in
// Update UI with received data
}
}
Complication
时间线提供程序
import ClockKit
struct ComplicationController: CLKComplicationDataSource {
func getComplicationDescriptors(handler: @escaping ([CLKComplicationDescriptor]) -> Void) {
let descriptor = CLKComplicationDescriptor(
identifier: "myComplication",
displayName: "My App",
supportedFamilies: [.circularSmall, .modularSmall, .graphicCircular]
)
handler([descriptor])
}
func getCurrentTimelineEntry(
for complication: CLKComplication,
withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void
) {
let template = makeTemplate(for: complication.family)
let entry = CLKComplicationTimelineEntry(date: .now, complicationTemplate: template)
handler(entry)
}
}
WidgetKit Complication(watchOS 9+)
import WidgetKit
import SwiftUI
struct MyComplication: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "MyComplication",
provider: ComplicationProvider()
) { entry in
ComplicationView(entry: entry)
}
.configurationDisplayName("My Complication")
.supportedFamilies([
.accessoryCircular,
.accessoryRectangular,
.accessoryCorner,
.accessoryInline
])
}
}
UI 组件
Digital Crown
@State private var crownValue = 0.0
ScrollView {
// Content
}
.focusable()
.digitalCrownRotation($crownValue)
触觉反馈
WKInterfaceDevice.current().play(.click)
WKInterfaceDevice.current().play(.success)
WKInterfaceDevice.current().play(.failure)
正在播放
import WatchKit
NowPlayingView() // Built-in now playing controls
健身应用
import HealthKit
@Observable
class WorkoutManager {
let healthStore = HKHealthStore()
var session: HKWorkoutSession?
var builder: HKLiveWorkoutBuilder?
func startWorkout(type: HKWorkoutActivityType) async throws {
let config = HKWorkoutConfiguration()
config.activityType = type
config.locationType = .outdoor
session = try HKWorkoutSession(healthStore: healthStore, configuration: config)
builder = session?.associatedWorkoutBuilder()
session?.startActivity(with: .now)
try await builder?.beginCollection(at: .now)
}
}
最佳实践
性能
- 使用
@Observable,而不是ObservableObject(watchOS 10+) - 限制后台刷新
- 在本地缓存图像
- 对列表使用延迟加载
电池
- 尽量减少位置更新
- 使用计划的后台任务
- 优先使用 Complication,而不是频繁刷新
- 批量发送网络请求
用户体验
- 始终显示加载状态
- 提供触觉反馈
- 支持键盘输入
- 使用清晰的图标
测试
模拟器
- 使用不同尺寸的 Watch 进行测试
- 验证所有样式中的 Complication
- 使用已配对的 iPhone 模拟器测试 Watch Connectivity
真机
- 测试对电池的影响
- 确认触觉反馈符合预期
- 在不同光照条件下测试
决策树
根据用户的需求选择合适的参考文件:
What are you building?
|
+- iPhone <-> Watch data sync
| -> watch-connectivity.md
| +- Session management, application context, real-time messaging
| +- File transfers, offline caching, complication push updates
|
+- Watch face complications
| -> complications.md
| +- ClockKit (legacy) vs WidgetKit (modern) complications
| +- Migration from ClockKit to WidgetKit
| +- Complication families (circular, rectangular, corner, inline)
| +- Timeline providers, reload strategies, gauges
|
+- Health / fitness / workout tracking
| -> health-fitness.md
| +- HealthKit authorization and data types
| +- HKWorkoutSession and HKLiveWorkoutBuilder
| +- Real-time heart rate, calories, distance
| +- Extended Runtime sessions, route tracking
|
+- watchOS widgets / Smart Stack
| -> widgets-for-watch.md
| +- Smart Stack configuration and relevance
| +- Cross-platform widget sharing (iOS + watchOS)
| +- watchOS-specific design (dark background, small screen)
|
+- General watchOS app development
-> This file (SKILL.md)
+- Design rules: ten-second test, layouts, navigation model, action buttons
+- App structure, navigation, lists
+- Digital Crown, haptics, Now Playing
参考文件
| 文件 | 内容 |
|---|---|
| watch-connectivity.md | iPhone <-> Watch 同步、会话管理、数据传输、离线缓存 |
| complications.md | 从 ClockKit 迁移到 WidgetKit、表盘复杂功能系列、时间线提供程序、仪表 |
| health-fitness.md | HealthKit、锻炼会话、心率、扩展运行时、路线跟踪、隐私 |
| widgets-for-watch.md | 智能叠放小组件、相关性、跨平台共享、watchOS 设计 |
外部参考
评论
支持 Markdown