Highlight
watchOS 8 把 Always-On Display 从表盘扩展到第三方应用,配合
isLuminanceReduced环境变量和TimelineView,让手表应用在垂腕时既能保持可见,又能控制更新频率、保护隐私。
核心内容
你开发了一个 Apple Watch 应用。用户抬腕时,一切正常。但垂腕后,屏幕要么黑掉,要么显示一个模糊的界面——这是 watchOS 7 的行为。
watchOS 8 改变了这个局面。
从 watchOS 8 开始,第三方应用也能在 Always-On 状态下保持可见。Apple Watch Series 5 及后续机型支持这个功能。用户不用抬腕,扫一眼就能看到应用内容。
这带来两个直接问题:
第一,低亮度下怎么设计 UI?
第二,垂腕时怎么更新内容?
Apple 用一套 API 回答了这两个问题。
详细内容
isLuminanceReduced:感知 Always-On 状态
(02:40)
watchOS 8 引入了新的 SwiftUI 环境属性:
@Environment(\.isLuminanceReduced) var isLuminanceReduced
当手表进入 Always-On 状态,系统会自动降低屏幕亮度。isLuminanceReduced 变为 true,你的应用可以据此调整 UI。
Xcode Preview 里也能模拟这个状态:
ContentView().environment(\.isLuminanceReduced, true)
(03:00)
关键点:
@Environment从系统环境中读取状态isLuminanceReduced在垂腕低亮度时为true- Preview 和模拟器都支持模拟该状态,模拟器窗口有专门按钮触发 wrist-down 事件
Apple 在 session 里展示了三个真实应用的设计做法。
Fitbod 保持了界面结构的一致性,只把次要文字进一步压暗。
AnyList 移除了列表项的背景色,把”希腊酸奶口味”、“牛奶数量”这类次要信息变暗,让主标题更突出。
Pandora 把专辑封面去掉,改用暗色描边,底部控制栏也压暗,只保留歌曲名和艺人名清晰可见。
(03:50)
敏感信息也要处理。银行账户余额、个人健康数据,在垂腕时应该模糊或隐藏。SwiftUI 的 .redacted(reason:) 修饰符和 widget 共用同一套模型。
动画需要重置到第一帧或静止状态,不要停在中间。
TimelineView:控制垂腕时的更新节奏
(06:59)
有活跃会话的应用(比如 workout 或音频播放),垂腕时每秒最多更新一次 UI。没有活跃会话的应用,每分钟最多一次。
SwiftUI 的 TimelineView 专门处理这种定时刷新。它接收一个 schedule,按时间构造视图。
watchOS 8 内置了五种 schedule:
EveryMinute:每分钟更新,对齐系统时钟,在整分触发Periodic:自定义间隔,比如每两分钟,不对齐系统时钟Explicit:在指定时间点更新,适合精确计时场景Custom:混合策略,比如倒计时前 59 分钟每分钟更新,最后 60 秒每秒更新Animation:动画专用
(07:18)
一个典型的 TimelineView 用法:
TimelineView(.everyMinute) { context in
Text(context.date, style: .time)
}
关键点:
TimelineView的闭包接收一个TimelineViewContext,包含当前日期- 非活跃会话下请求快于每分钟更新,系统会尝试但不保证
- 默认两分钟后系统会切回表盘,但用户可以在设置里改,应用要做好长时间可见的准备
HealthKit 后台数据投递
(08:58)
watchOS 8 把 iOS 上已有的 HealthKit 后台投递带到了 Watch 应用。设置方式和 iOS 一样:
let query = HKObserverQuery(
sampleType: heartRateType,
predicate: nil
) { query, completionHandler, error in
fetchLatestHeartRateData()
completionHandler()
}
healthStore.execute(query)
healthStore.enableBackgroundDelivery(
for: heartRateType,
frequency: .immediate
) { success, error in
// 启用后台投递
}
(09:13)
关键点:
HKObserverQuery监听数据变化enableBackgroundDelivery开启后台唤醒- 普通应用每小时最多唤醒一次
- 如果 complications 在活跃表盘上,每小时最多四次
- 所有唤醒次数计入后台刷新预算,上限是每小时四次
- 跌倒事件、低血氧、心率异常等关键数据类型会立即投递
- 其他数据类型按小时或更长间隔投递
HealthKit 授权表单也更新了,会告知用户应用将在 Apple Watch 上更频繁地访问健康数据。
蓝牙设备后台连接
(10:49)
watchOS 4 开始支持蓝牙设备直连 Apple Watch。watchOS 8 的新能力是在后台刷新期间连接蓝牙设备。
这意味着 complications 可以持续从蓝牙配件获取数据。比如 Qardio 的医疗级 ECG、Sonra Watch 的足球运动员 GPS 追踪器。
后台刷新每小时最多提供四次连接机会,同样计入后台刷新预算。
Apple 给 WKRefreshBackgroundTask 加了 expiration handler:
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
task.expirationHandler = {
// 时间快用完了,清理连接
}
// 连接蓝牙设备、获取数据
}
}
(12:35)
关键点:
- 蓝牙设备初始连接必须在应用前台完成,后台不能建立新连接
- 连接建立后,后台刷新期间可以重连获取数据
- 数据处理必须在短时间内完成
expirationHandler让你能在时间耗尽前优雅收尾
基于地理位置的区域通知
(13:15)
watchOS 8 支持 UNLocationNotificationTrigger,让用户进入或离开指定区域时收到通知。
let center = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)
let region = CLCircularRegion(
center: center,
radius: 200,
identifier: "gym"
)
region.notifyOnEntry = true
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
let content = UNMutableNotificationContent()
content.title = "到达健身房"
content.body = "开始记录你的训练吧"
let request = UNNotificationRequest(
identifier: "gym-entry",
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request)
(13:40)
关键点:
CLCircularRegion定义圆形地理围栏UNLocationNotificationTrigger基于区域触发通知- 需要 “When in use” 位置权限
- 用户先收到静态通知,点击后才显示完整动态内容,保护隐私
- 区域半径建议几百米,只设置用户明确感兴趣的位置
Core Location 的 location button 也来到了 watchOS。用户点击按钮即授予一次性位置权限,不用每次都弹窗授权。
其他系统增强
(15:18)
watchOS 8 还有一些值得注意的改进:
- 睡眠呼吸率:Apple Watch 可以在睡眠时测量呼吸率,应用可以通过 HealthKit 读取
- AssistiveTouch:通过手势识别帮助上肢差异用户操作手表
- 大号辅助文字:更大的无障碍文字尺寸
- 大标题:滚动视图顶部支持 iOS 风格的大标题
- 文本输入优化:记住用户每个应用的 Scribble / 听写偏好,支持快速切换
- 搜索 API:
searchable修饰符支持自定义搜索建议 - 列表滑动操作:列表项支持自定义滑动操作(如收藏)
- Button roles:区分按钮类型(如 destructive),支持控制 prominence 和额外触觉反馈
- Canvas API:SwiftUI Canvas 来到 watchOS,支持 GPU 程序化绘制
核心启发
-
做一个垂腕可见的计时器应用。利用
TimelineView+EveryMinuteschedule,让用户在做饭或健身时垂腕也能看到剩余时间。最后 60 秒切换到每秒更新。入口 API:TimelineView+Customschedule。 -
做一个蓝牙血糖仪配套 complication。在后台刷新期间连接血糖仪,每小时更新一次表盘 complication。利用
expirationHandler确保连接及时关闭。入口 API:CBCentralManager+WKRefreshBackgroundTask。 -
做一个基于位置的健身提醒应用。用户到达健身房自动提醒开始记录训练,离开提醒保存数据。利用
UNLocationNotificationTrigger设置 200 米半径的地理围栏。入口 API:CLCircularRegion+UNLocationNotificationTrigger。 -
做一个睡眠呼吸监测 dashboard。利用 watchOS 8 新增的睡眠呼吸率数据,每天早上展示呼吸率变化趋势。结合 HealthKit 后台投递,无需用户手动刷新。入口 API:
HKQuantityType(.respiratoryRate)+HKObserverQuery。 -
做一个 Always-On 友好的音乐播放界面。垂腕时隐藏专辑封面和播放控制,只显示歌曲名和艺人名。利用
isLuminanceReduced控制不同状态下的 UI 层级。入口 API:@Environment(\.isLuminanceReduced)+.redacted(reason:)。
关联 Session
- Build a workout app for Apple Watch — 从零开始用 SwiftUI 和 HealthKit 构建 workout app,包含 TimelineView 和 Always-On 支持的实际代码
- Connect Bluetooth devices to Apple Watch — 深入讲解蓝牙设备后台连接的完整实现
- There and back again: Data transfer on Apple Watch — Apple Watch 数据通信策略,包括 Watch Connectivity 和 URLSession
- What’s new in SwiftUI — SwiftUI 新特性,包括 TimelineView 的更多用法
评论
GitHub Issues · utterances