Highlight
Apple 在 iOS 15 中把 U1 空间感知和 Nearby Interaction 能力开放给第三方硬件,开发者可以设计跨设备、带距离和方向反馈的近场交互。
核心内容
过去,很多跨设备操作都要靠列表完成。
你想把 iPhone 上的音乐切到客厅的 HomePod,需要打开 AirPlay 列表。列表里每个扬声器都排在一起,近处的设备和远处的设备看起来没有差别。用户真正想表达的是“把这首歌放到这个扬声器上”,界面却要求他先读列表,再选择名称。
找东西也类似。地图只能把你带到大致位置。到了房间里,你还要靠眼睛、耳朵和猜测继续找。对于藏在沙发缝、包里、桌子底下的物品,屏幕上的抽象位置帮助有限。
Apple 在这场 session 里总结了 AirTag、HomePod mini 和 iPhone 的设计经验。核心变化来自 U1 芯片带来的空间感知,以及 iOS 15 中面向第三方硬件的新 API。设备可以理解附近物体的距离、方向和相对关系,应用可以把这些信息转成视觉、声音和触觉反馈。
这改变了交互的入口。用户不必先理解列表、按钮和设备名称。应用可以让用户移动身体、靠近目标、转向目标,然后用连续反馈告诉他下一步该做什么。
距离决定界面
AirTag 的查找体验展示了第一个原则:同一个任务在不同距离下需要不同界面。
距离很远时,地图和路线有用。进入附近范围后,按钮从“获取路线”变成“查找”。连接成功后,箭头开始指向目标。靠近到手臂范围内时,箭头让位给触觉反馈,因为毫米级移动更适合用手感判断。
设计近场交互时,不要把一个界面从头用到尾。远距离需要方向。近距离需要精度。到了最后几厘米,屏幕的优先级会下降。
反馈必须连续
HomePod mini 的音乐传输展示了第二个原则:反馈要跟着身体动作连续变化。
iPhone 靠近 HomePod mini 时,屏幕上的横幅位置、大小和背景模糊会随距离变化。HomePod 顶部灯光也会响应靠近动作。用户继续靠近会确认传输,拉远会取消操作。
这里没有额外的“确认”按钮。身体动作本身承担了确认和取消。连续反馈让用户知道系统正在理解他的动作。
界面要服务现实动作
AirTag 查找时,用户的注意力在房间里。屏幕只是辅助。
所以 Apple 使用大箭头、大距离数字、明显颜色变化、声音和触觉。用户可以用余光读到方向,也可以通过声音判断 AirTag 在空间里的位置。进入手臂范围后,触觉反馈比声音更适合引导最后的定位。
这类设计的目标是减少盯屏时间。界面要把注意力还给物理世界。
详细内容
这场 session 是设计导向,没有 Code tab。下面的代码把 transcript 中的设计原则落成可实现的应用状态。API 层面,Resources 指向 Nearby Interaction framework;设计层面,演讲强调距离、方向、视觉、声音、触觉和中断。
1. 按设备能力降级
(02:48)Apple 先用分享面板说明空间感知的降级策略。所有设备都可以用 Bluetooth 和 Wi-Fi 查找附近的人。带 U1 的 iPhone 可以进一步优先显示用户正面对着或距离很近的人。
import Foundation
struct ShareCandidate {
let name: String
let communicationScore: Double
let isNearby: Bool
let isFacing: Bool
}
enum SpatialAwarenessLevel {
case bluetoothAndWiFi
case u1DirectionAware
}
func rankCandidates(
_ candidates: [ShareCandidate],
level: SpatialAwarenessLevel
) -> [ShareCandidate] {
candidates.sorted { first, second in
score(first, level: level) > score(second, level: level)
}
}
func score(_ candidate: ShareCandidate, level: SpatialAwarenessLevel) -> Double {
var result = candidate.communicationScore
if candidate.isNearby {
result += 10
}
if level == .u1DirectionAware && candidate.isFacing {
result += 20
}
return result
}
let people = [
ShareCandidate(name: "Arian", communicationScore: 42, isNearby: true, isFacing: false),
ShareCandidate(name: "Taylor", communicationScore: 30, isNearby: true, isFacing: true),
ShareCandidate(name: "Linus", communicationScore: 50, isNearby: false, isFacing: false)
]
let ranked = rankCandidates(people, level: .u1DirectionAware)
print(ranked.map(\.name))
关键点:
ShareCandidate保存候选人的通信频率、是否附近、是否正对这些输入。SpatialAwarenessLevel把设备能力分成基础近场能力和 U1 方向感知能力。rankCandidates只负责排序,界面可以复用同一组候选人。score在基础设备上只增加附近权重。level == .u1DirectionAware时才使用isFacing。- 这个结构对应 transcript 中“能力并非所有设备都有,设计要适配不同能力”的要求。
2. 根据距离切换主反馈
(04:00)AirTag 查找过程会随距离改变。远处用地图和方向。连接后用箭头。靠近时,朝向判断更严格。进入手臂范围后,触觉成为主反馈。
import Foundation
enum PrimaryFeedback: String {
case map
case arrow
case haptics
}
struct FindingFeedback {
let primaryFeedback: PrimaryFeedback
let facingToleranceInDegrees: Double
let showsDistance: Bool
}
func feedbackForFindingItem(distanceInMeters: Double) -> FindingFeedback {
if distanceInMeters > 10 {
return FindingFeedback(
primaryFeedback: .map,
facingToleranceInDegrees: 90,
showsDistance: true
)
}
if distanceInMeters > 1 {
return FindingFeedback(
primaryFeedback: .arrow,
facingToleranceInDegrees: 35,
showsDistance: true
)
}
return FindingFeedback(
primaryFeedback: .haptics,
facingToleranceInDegrees: 15,
showsDistance: false
)
}
let far = feedbackForFindingItem(distanceInMeters: 12)
let near = feedbackForFindingItem(distanceInMeters: 0.6)
print(far.primaryFeedback.rawValue)
print(near.primaryFeedback.rawValue)
关键点:
PrimaryFeedback表示当前阶段最重要的反馈形式。distanceInMeters > 10时返回.map,对应“大致位置”的阶段。distanceInMeters > 1时返回.arrow,对应箭头引导阶段。- 最后返回
.haptics,对应手臂范围内的精细查找。 facingToleranceInDegrees随距离缩小,表达演讲中“远处角度更宽容,近处更精确”的设计。showsDistance在最后阶段关闭,避免屏幕数字抢走对触觉的注意力。
3. 用连续反馈表达确认和取消
(07:23)HomePod mini 的传输体验使用两个距离边界。到达第一个边界时开始反馈。继续靠近会进入确认区。拉远可以自然取消。
import Foundation
enum TransferPhase: String {
case idle
case ready
case confirming
case completed
}
struct TransferFeedback {
let phase: TransferPhase
let bannerScale: Double
let backgroundBlur: Double
let hapticIntensity: Double
}
func transferFeedback(distanceToSpeaker: Double) -> TransferFeedback {
let firstThreshold = 0.6
let secondThreshold = 0.12
if distanceToSpeaker > firstThreshold {
return TransferFeedback(
phase: .idle,
bannerScale: 0,
backgroundBlur: 0,
hapticIntensity: 0
)
}
if distanceToSpeaker > secondThreshold {
let progress = (firstThreshold - distanceToSpeaker) / (firstThreshold - secondThreshold)
return TransferFeedback(
phase: .ready,
bannerScale: 0.8 + progress * 0.2,
backgroundBlur: progress,
hapticIntensity: progress
)
}
return TransferFeedback(
phase: .completed,
bannerScale: 1,
backgroundBlur: 1,
hapticIntensity: 1
)
}
let movingCloser = transferFeedback(distanceToSpeaker: 0.3)
let reachedSpeaker = transferFeedback(distanceToSpeaker: 0.08)
print(movingCloser.phase.rawValue)
print(reachedSpeaker.phase.rawValue)
关键点:
firstThreshold是开始反馈的边界。secondThreshold是确认传输的边界。- 距离大于第一个边界时,状态是
.idle,界面不抢注意力。 - 两个边界之间计算
progress,让横幅、模糊和触觉连续变化。 - 距离小于第二个边界时,状态进入
.completed。 - 用户把 iPhone 拉远后,函数会回到
.idle或.ready,这就是自然取消。
4. 为余光阅读设计界面文本
(15:23)AirTag 查找时,用户的注意力应该放在周围环境。Apple 使用更大的距离文字、中心箭头、明显颜色变化、声音和触觉,让用户不必长时间盯着屏幕。
import Foundation
enum FindingState {
case connecting
case pointing(distanceInMeters: Double, isFacingTarget: Bool)
case withinReach
case signalLost
}
struct PeripheralMessage {
let title: String
let detail: String
let usesLargeType: Bool
let shouldPlaySound: Bool
let shouldUseHaptics: Bool
}
func message(for state: FindingState) -> PeripheralMessage {
switch state {
case .connecting:
return PeripheralMessage(
title: "正在连接",
detail: "移动 iPhone 搜索附近物品",
usesLargeType: true,
shouldPlaySound: false,
shouldUseHaptics: false
)
case .pointing(let distance, let isFacingTarget):
return PeripheralMessage(
title: String(format: "%.1f 米", distance),
detail: isFacingTarget ? "继续向前" : "转动 iPhone 寻找方向",
usesLargeType: true,
shouldPlaySound: isFacingTarget,
shouldUseHaptics: isFacingTarget
)
case .withinReach:
return PeripheralMessage(
title: "就在附近",
detail: "缓慢移动 iPhone,跟随触觉反馈",
usesLargeType: true,
shouldPlaySound: false,
shouldUseHaptics: true
)
case .signalLost:
return PeripheralMessage(
title: "信号丢失",
detail: "回到刚才的位置重新连接",
usesLargeType: true,
shouldPlaySound: false,
shouldUseHaptics: false
)
}
}
let state = FindingState.pointing(distanceInMeters: 2.4, isFacingTarget: true)
let currentMessage = message(for: state)
print(currentMessage.title)
print(currentMessage.detail)
关键点:
FindingState覆盖连接、指向、手臂范围内、信号丢失四个状态。PeripheralMessage把文案、字号策略、声音和触觉放在一起。.pointing状态显示大号距离,让用户用余光读取。isFacingTarget为真时才播放声音和触觉,形成稳定的因果关系。.withinReach关闭声音,使用触觉反馈,对应 transcript 中近距离查找不用声音的说明。.signalLost给出恢复动作,避免用户在中断后不知道下一步。
5. 让反馈跨感官配合
(12:45)Apple 建议把视觉反馈作为连续层,用声音和触觉强调关键时刻。声音和触觉要有清楚、可重复的因果关系。
import Foundation
struct FeedbackCue {
let visualProgress: Double
let soundName: String?
let hapticPattern: String?
}
func cueForArrowAlignment(wasAligned: Bool, isAligned: Bool, distanceInMeters: Double) -> FeedbackCue {
let visualProgress = max(0, min(1, 1 - distanceInMeters / 5))
if !wasAligned && isAligned {
return FeedbackCue(
visualProgress: visualProgress,
soundName: "direction-lock",
hapticPattern: "snap"
)
}
if isAligned {
return FeedbackCue(
visualProgress: visualProgress,
soundName: nil,
hapticPattern: "pulse"
)
}
return FeedbackCue(
visualProgress: visualProgress,
soundName: nil,
hapticPattern: nil
)
}
let cue = cueForArrowAlignment(
wasAligned: false,
isAligned: true,
distanceInMeters: 3
)
print(cue.visualProgress)
print(cue.soundName ?? "no sound")
print(cue.hapticPattern ?? "no haptics")
关键点:
visualProgress是持续变化的视觉层,距离越近数值越大。!wasAligned && isAligned表示刚刚对准目标。- 刚对准时返回声音
direction-lock和触觉snap,对应演讲中的“箭头归位”时刻。 - 持续对准时只保留
pulse,避免重复播放声音。 - 偏离方向时关闭声音和触觉,让反馈和动作保持一一对应。
核心启发
1. 做一个“靠近即切换”的音频控制
- 做什么:用户拿着 iPhone 靠近某个音箱时,界面自动显示传输提示,继续靠近完成播放切换,拉远取消。
- 为什么值得做:HomePod mini 的例子证明,距离边界和连续反馈可以替代设备列表。
- 怎么开始:先把附近设备抽象成
TransferPhase,用距离更新横幅、模糊和触觉强度。硬件层接入 Nearby Interaction 或你已有的近场发现能力。
2. 做一个仓库物品查找工具
- 做什么:给工具箱、样品盒或仓储标签加支持近场定位的配件,用 iPhone 引导员工找到目标。
- 为什么值得做:AirTag 的查找流程适合“先到大致区域,再精确定位”的任务。
- 怎么开始:把流程拆成地图、箭头、触觉三个阶段。远距离显示区域,近距离显示方向,手臂范围内改用触觉扫描。
3. 做一个“面对谁就分享给谁”的团队协作入口
- 做什么:会议中打开分享面板时,优先显示你面前或附近的同事。
- 为什么值得做:演讲提到分享面板会结合面对方向、物理距离、通信频率和最近通信记录预测收件人。
- 怎么开始:先用通信频率和附近状态排序;在支持 U1 的设备上再加入朝向权重。能力不足时保留原有列表。
4. 做一个不用盯屏的导航辅助
- 做什么:在展馆、医院或校园里,用大号方向提示、声音和触觉引导用户找房间。
- 为什么值得做:session 强调空间交互要让用户把注意力放回周围环境。
- 怎么开始:为每个导航状态生成
PeripheralMessage。界面使用大字号和高对比色,关键状态用声音和触觉确认。
5. 做一个可中断的设备配对流程
- 做什么:用户靠近配件开始配对,继续靠近确认,移开设备取消。
- 为什么值得做:HomePod mini 的双边界设计让确认和取消都来自自然身体动作。
- 怎么开始:定义两个距离阈值。第一个阈值启动反馈,第二个阈值执行操作。距离回退时撤销准备状态,并给出清楚反馈。
关联 Session
- Explore Nearby Interaction with third-party accessories — 讲解 Nearby Interaction framework 如何连接 U1 设备和第三方 UWB 配件。
- Practice audio haptic design — 深入声音、触觉和动画如何配合交互关键时刻。
- The practice of inclusive design — 帮助你检查空间交互中的文案、视觉和体验是否服务更多用户。
- The process of inclusive design — 从团队和流程角度补足空间交互的包容性设计方法。
评论
GitHub Issues · utterances