mcpField
W
watchOS

watchOS

github.com/rshankras开发838581 次安装14 周前更新
代码仓库

关于 watchOS

面向 watchOS 应用开发的指导技能,适用于构建 Watch 应用、扩展和表盘复杂功能。内容涵盖 SwiftUI 界面、设备间数据传输、WidgetKit、HealthKit 与无障碍设计。

主要功能

  1. 01Watch 优先的界面与导航指导
  2. 02Watch Connectivity 数据传输
  3. 03ClockKit 与 WidgetKit 复杂功能
  4. 04HealthKit 锻炼与后台运行
  5. 05watchOS 无障碍设计
  6. 06性能与电池使用建议

适用场景

  1. 01设计适合快速查看的 Watch 界面
  2. 02同步 iPhone 与 Watch 应用数据
  3. 03创建表盘复杂功能和 Smart Stack 小组件
  4. 04实现锻炼追踪与无障碍交互

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rshankras/claude-code-apple-skills --skill watchos

安装后在 Claude Code 中输入/watchos确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

complications.md
health-fitness.md
watch-connectivity.md
widgets-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.mdiPhone <-> Watch 同步、会话管理、数据传输、离线缓存
complications.md从 ClockKit 迁移到 WidgetKit、表盘复杂功能系列、时间线提供程序、仪表
health-fitness.mdHealthKit、锻炼会话、心率、扩展运行时、路线跟踪、隐私
widgets-for-watch.md智能叠放小组件、相关性、跨平台共享、watchOS 设计

外部参考

评论

支持 Markdown