WWDC Quick Look 💓 By SwiftGGTeam
Elevate your windowed app for spatial computing

Elevate your windowed app for spatial computing

观看原视频

Highlight

Apple 在 visionOS 上为 SwiftUI 提供了自动适配的玻璃背景、动态缩放、vibrancy 材质和 hover effect 等能力,让现有的多平台 SwiftUI 应用几乎零改动就能在 Shared Space 中运行,同时通过 ornament、vector asset 等少量调整即可充分利用空间计算平台的独特交互方式。

核心内容

从 iPad 到 visionOS:一步之遥

你有一个支持 iPhone、iPad、Mac 和 Apple Watch 的 SwiftUI 应用,想让它在 visionOS 上运行。过去,适配一个新平台意味着重写大量 UI 代码。visionOS 不同——SwiftUI 从设计之初就考虑了这个平台。

在 Xcode 的 Supported Platforms 菜单中添加 visionOS,应用就能在模拟器中运行。布局保持 iPad 风格,但细节已经变了:窗口有了玻璃背景,导航栏按钮变成圆形,列表行在注视时会高亮。这些变化全部由 SwiftUI 自动处理。

02:51

玻璃与 Vibrancy:没有 Light/Dark 的世界

visionOS 不区分浅色和深色模式。玻璃背景会根据环境光线和背后物体的颜色自动调整对比度。这意味着你不能再依赖 colorScheme 检查来为不同模式设置不同颜色。

Solid color(纯色)在玻璃上表现固定,无法适应环境变化。SwiftUI 的 semantic styles(语义化样式)会自动采用 vibrancy 材质,在玻璃上保持可读性。将自定义视图的背景从纯色改为 .fill 等语义样式后,代码更简洁,视觉效果在所有平台上都更正确。

06:58

Hover Effect:空间交互的视觉反馈

visionOS 有四种交互方式:间接捏合(最常用)、直接触摸、触控板指针、辅助功能。无论哪种方式,可交互内容都需要 hover effect 来提供视觉反馈。

SwiftUI 的系统控件自动带有 hover effect。自定义的可交互视图需要手动添加:

// 为自定义可交互视图添加 hover effect
BirdView()
    .hoverEffect()

关键点:

  • hoverEffect() 默认提供高亮效果,注视时视图会微微变亮
  • 系统 hover effect 在应用进程外执行,保护用户隐私——应用无法直接获取用户的注视位置
  • 应用只能收到捏合、触摸、悬停手指或指针的事件

如果 hover effect 的边界形状不理想,可以用 contentShape 修饰符自定义:

BirdView()
    .contentShape(.hoverEffect, RoundedRectangle(cornerRadius: 12))
    .hoverEffect()

关键点:

  • contentShape(.hoverEffect, ...) 指定 hover effect 的形状和边界
  • 这里使用圆角矩形,让 hover effect 有圆角和适当内边距

更好的做法是将可交互视图直接改为 Button

Button(action: showBirdDetails) {
    BirdView()
}
.buttonStyle(.plain)

关键点:

  • Button 在 visionOS 上默认有边框,使用 .plain 样式可去掉背景
  • Plain button 自动获得标准 hover effect 和点击缩放动画

10:16

Ornament:窗口外的控件

visionOS 的窗口不受屏幕尺寸限制,控件也不必局限在窗口内部。Ornament 是一种附着在窗口边缘的控件容器,常用于放置辅助功能。

TabView 在 visionOS 上会自动变成 ornament,附着在窗口左侧。平时收起,注视图标时展开显示标签。

Toolbar 也可以通过 .bottomOrnament placement 变成 ornament:

.toolbar {
    ToolbarItem(placement: .bottomOrnament) {
        TimeRangePicker()
    }
}

自定义 ornament 使用 ornament() 修饰符:

.ornament(
    attachmentAnchor: .scene(.bottom),
    contentAlignment: .center
) {
    NotificationBanner()
        .glassBackgroundEffect()
}

关键点:

  • attachmentAnchor: .scene(.bottom) 将 ornament 附着在窗口底部
  • contentAlignment: .center 让 ornament 中心与锚点对齐
  • 自定义 ornament 默认没有玻璃背景,需要手动添加 .glassBackgroundEffect()

13:34

详细内容

Vector Asset:动态缩放的关键

visionOS 中的应用可能离你很远,也可能被拉近到眼前,还可能从侧面观看。系统通过动态缩放确保内容在任何距离都清晰。这要求你提供矢量资源。

文本和 SF Symbols 已经是矢量。自定义图标和图形需要在 Asset Catalog 中设置为 Single Scale,并勾选 Preserve Vector Data:

Asset Catalog 设置:
- Scales: Single Scale
- Preserve Vector Data: ✅

Bitmap 图片在放大时会模糊。将位图替换为矢量图后,无论用户靠近还是远离,图形始终保持锐利。

05:52

从 NavigationSplitView 到 TabView

iPad 上,Regular 水平尺寸类优先使用 sidebar。visionOS 上窗口尺寸不受限制,sidebar 占用内容空间。TabView 是更好的选择:

TabView {
    BackyardsView()
        .tabItem {
            Label("Backyards", systemImage: "tree")
        }
    PlantsView()
        .tabItem {
            Label("Plants", systemImage: "leaf")
        }
    BirdsView()
        .tabItem {
            Label("Birds", systemImage: "bird")
        }
}

关键点:

  • TabView 在 visionOS 上自动显示为左侧 ornament
  • 每个 tab item 需要标题和图标
  • 未交互时 tab bar 收起,节省内容空间

12:15

核心启发

  • 做什么:将现有的 iPad SwiftUI 应用移植到 visionOS

    • 为什么值得做:只需在 Xcode 中添加 visionOS 目标,应用就能在 Shared Space 中运行。SwiftUI 自动处理玻璃背景、控件适配和 hover effect,移植成本极低
    • 怎么开始:打开项目设置,在 Supported Platforms 中添加 visionOS,在模拟器中运行并观察自动适配效果
  • 做什么:为应用设计一个自定义 ornament 通知系统

    • 为什么值得做:ornament 可以附着在窗口外部,不占用内容区域。适合放置状态提示、快捷操作等辅助信息
    • 怎么开始:使用 .ornament(attachmentAnchor:contentAlignment:content:) 修饰符,结合 .glassBackgroundEffect() 创建悬浮通知
  • 做什么:用矢量图形替换应用中的所有位图图标

    • 为什么值得做:visionOS 上应用可能被用户拉近到眼前,位图会暴露像素。矢量图在任何缩放级别都保持清晰
    • 怎么开始:检查 Asset Catalog,将多 scale 位图替换为 Single Scale 矢量图,勾选 Preserve Vector Data
  • 做什么:审查应用中所有基于 colorScheme 的条件渲染逻辑

    • 为什么值得做:visionOS 不区分 light/dark,且玻璃背景会动态变化。solid color 无法适应环境,semantic styles 和 vibrancy 才是正确方案
    • 怎么开始:搜索代码中的 .colorSchemeColor(...) 硬编码,替换为 .fill.background 等语义化样式
  • 做什么:为所有自定义可交互控件添加 hover effect

    • 为什么值得做:空间计算中用户通过注视选择目标,hover effect 是确认”这个控件可以交互”的关键视觉反馈。缺少它会让用户感到困惑
    • 怎么开始:优先将可点击视图改为 Button,对无法改用的视图添加 .hoverEffect().contentShape(.hoverEffect, ...)

关联 Session

评论

GitHub Issues · utterances