Design hover interactions for visionOS
Spatial Computing 进阶 0m

设计 visionOS 悬停交互

Design hover interactions for visionOS

2025年6月9日

在 Apple 官方观看视频

一句话判断

visionOS 的 hover 效果不是简单的 hover 态 CSS——它是隐私保护下的状态机动画、眼动驱动的滚动、以及媒体控件的持久化显示,这三个机制合在一起才是完整的交互模型。

这场 Session 讲了什么

Session 讲了 visionOS 上三种基于眼动追踪的交互机制:自定义 hover 效果(Custom Hover Effects)、Look to Scroll(眼动滚动)、以及持久化控件(Persistent Controls)。

核心背景是 visionOS 的隐私架构:系统不告诉你用户在看哪里——它只在用户注视某个 View 时帮你从”标准状态”切换到”hover 状态”,动画由系统驱动。这意味着你不能用 hover 来触发”动作”(比如点击下载),只能用来做”动画”(比如显示下载大小)。这个限制一开始可能让人困惑,但它是有道理的。

值得深挖的点

三种动画节奏

Session 把 hover 动画分成三类,这个分类非常实用:

Instant(即时):用户一看到就立刻展示,比如播放器进度条上的时间戳、按钮上的箭头图标。适合小的、上下文性的、非交互的信息。

Delayed(延迟):短暂延迟后才展开,比如 tooltip。延迟让用户可以快速点击按钮而不被 tooltip 干扰——只有用户”持续关注”时才显示。

Ramp(渐进):前两者之间的折中。开始时缓慢变化(让用户感知到”可以展开”),然后快速弹开到完整状态。Home View 的环境图标就是这个效果——你扫过图标时它微微放大,持续注视时才完全展开。这个节奏在大面积网格浏览时特别有用,避免图标跟着视线到处弹开。

锚定元素原则

最好的 hover 效果总有一部分保持不变——这些”锚定元素”给用户上下文,帮助他们理解什么变了。如果文字在 hover 时移动,会打断用户的阅读。如果整个 View 完全变样,用户会困惑自己在看什么。

同样,hover 效果应该从一个可见元素出发。如果你有隐藏元素需要在 hover 时显示(比如窗口角落的缩放手柄),让它在 hover 可见元素时出现,而不是让隐藏元素本身可被 hover。

Look to Scroll 的适用边界

Look to Scroll 不是所有 ScrollView 都应该启用。判断标准:如果 View 主要用于阅读或浏览(文章、视频列表),启用。如果主要是 UI 控件(设置页面),不启用。TV App 的”相关节目”列表和主库列表外观一致,用户期望行为也一致,所以都启用。

技术上,启用 Look to Scroll 的 ScrollView 最好占满窗口的宽或高,给用户充足的滚动区域和明确的边缘。如果 ScrollView 有内缩,提供清晰的边界让用户知道在哪里看可以触发滚动。

Safari Tab 关闭按钮的教训

Session 给了一个绝佳的反面案例:Safari 的标签页概览。如果把关闭按钮设为”只在 hover 时显示”,用户看标签页时按钮出现,眼睛会本能地跳到按钮上,此时做点击手势就可能误关标签页。正确做法是:hover 标签页时按钮半透明渐入,再 hover 按钮本身时才完全显示。这个案例说明 hover 效果必须在真机上测试——视频和模拟器都无法还原眼动交互。

代码片段

1. 自定义 Hover 效果类型

场景:按钮 hover 时显示额外信息(Instant 风格)。

// SwiftUI 中的自定义 hover 效果
struct DownloadButton: View {
    @State private var isHovered = false

    var body: some View {
        ZStack {
            Image(systemName: "arrow.down.circle")
            if isHovered {
                Text("2.4 MB")
                    .font(.caption)
                    .transition(.opacity)
            }
        }
        .hoverEffect { effect, isActive, _ in
            // Instant: 立即显示
            withAnimation(isActive ? .easeOut(duration: 0.15) : .easeIn(duration: 0.1)) {
                isHovered = isActive
            }
        }
    }
}

坑:不要对大型 View(比如整张照片)做自定义 hover 效果——大面积运动会导致视觉不适。保持效果在小视图上。

2. 持久化媒体控件

场景:自定义视频播放器在用户看控件时保持显示。

// visionOS 26 中,标准 AVPlayerViewController 自动支持
// 自定义控件需要显式启用
VideoPlayer(player: player)
    .persistentSystemOverlays(.visible) // 伪代码,实际用对应 API

最佳实践

从标准 highlight 效果开始,只在确实需要更多信息传达时才用自定义 hover。高使用频率的 View(工具栏按钮、列表行)用标准效果就够——自定义的缩放或展开在频繁交互时反而让人分心。

Ramp 动画是最佳默认选择——它提供即时反馈(让用户知道可以交互)但不会过度反应(不会在快速扫视时到处弹开)。曲线建议用”慢入快弹”:先 ease-in 缓慢变化,再用 spring 快速弹开到完成态。

必须在真机上测试。这一点再怎么强调都不为过。hover 效果与眼动追踪绑定,Simulator 无法体验,看视频也看不出问题。设计 hover 是一个高度迭代的过程——多试几个方案,花时间调参。

还有什么值得关注

  • Look to Scroll 和持久化控件在 developer.apple.com 的文档中有详细 API 说明,不需要额外的 Session。
  • SwiftUI 和 RealityKit 构建自定义 hover 效果的 2024 Session:如果还没看,是理解底层机制的必修课。
  • 3D 对象的 hover:highlight 效果适用于可选中的 3D 对象,先高亮再渐隐以显示真实颜色,和 2D 图片的处理逻辑一致。
空间计算 Design