WWDC Quick Look 💓 By SwiftGGTeam
Meet SwiftUI for spatial computing

Meet SwiftUI for spatial computing

观看原视频

Highlight

SwiftUI 是构建 visionOS 应用的最佳方式,提供三种场景类型(Window、Volume、ImmersiveSpace)、全新的 3D 能力(Model3D、RealityView、3D 手势)以及自动适配平台的智能默认值,从 Button 的悬停效果到 TabView 的侧边导航都无需额外配置。

核心内容

Apple 用 SwiftUI 从零构建了 visionOS 的整个系统界面。Home View、Control Center、Safari、Freeform 的 3D 画板、Keynote 的沉浸式排练——全部用 SwiftUI 实现。

00:38)这意味着你的 SwiftUI 知识可以直接迁移到这个新平台。Button、Toggle、TabView 等基础控件都保留了熟悉的 API,但获得了平台特有的外观和交互。

三种场景类型

03:49)visionOS 应用由三种场景构成:

  • Window:传统 2D 窗口,玻璃背景让环境透过来。适合 Safari、Freeform 这类内容型应用。
  • Volume:新的 3D 窗口样式,在有限空间中展示 3D 对象。可以和其他应用并列显示。适合 3D 模型预览、棋盘游戏。
  • ImmersiveSpace(Full Space):完全沉浸式的 3D 环境。可以隐藏周围环境,把用户带入全新的世界。适合游戏、沉浸式体验。

这三种场景可以混合使用。从 Window 中打开 Volume 展示 3D 预览,再从 Volume 进入 Full Space 完全沉浸。

平台适配的智能默认值

02:02)Button 默认使用 bordered 样式和 vibrant 材质背景,支持注视触发的悬停效果,按下时缩放并提供音频反馈。导航栏中的按钮会在用户注视时自动显示工具提示。

TabView 悬挂在窗口左侧边缘,注视时展开显示标签文字。这充分利用了平台”注视即交互”的特性。

3D 能力

14:10Model3DImage 一样简单,异步加载和显示 3D 模型。RealityView 提供完整的 RealityKit 能力,支持自定义材质、物理、动画。3D 手势如 SpatialTapGestureRotateGesture3D 让交互更自然。

详细内容

应用入口(App Protocol)

03:37)visionOS 应用使用与其他平台相同的 App protocol:

@main
struct WorldApp: App {
    var body: some Scene {
        WindowGroup("Hello, world") {
            ContentView()
        }
    }
}

关键点:

  • WindowGroup 自动支持多窗口,和 macOS/iPadOS 一样
  • 应用可以同时定义多个场景

带 TabView 的窗口

07:03)使用 TabView 组织应用内容:

@main
struct WorldApp: App {
    var body: some Scene {
        WindowGroup("Hello, world") {
            TabView {
                Modules()
                    .tag(Tabs.menu)
                    .tabItem {
                        Label("Experience", systemImage: "globe.americas")
                    }
                FunFactsTab()
                    .tag(Tabs.library)
                    .tabItem {
                        Label("Library", systemImage: "book")
                    }
            }
        }
    }
}

关键点:

  • TabView 自动显示在窗口左侧边缘
  • 注视时展开显示完整标签文字
  • 使用 .tag 标识当前选中的标签

材质和玻璃背景

08:42)使用系统材质创建视觉层次:

VStack(alignment: .leading, spacing: 12) {
    Text("Stats")
        .font(.title)

    StatsGrid(stats: stats)
        .padding()
        .background(.regularMaterial, in: .rect(cornerRadius: 12))
}

关键点:

  • 窗口默认使用玻璃背景,没有明暗模式之分
  • .regularMaterial 在玻璃上添加 vibrant 深色背景
  • 材质自动适应环境,保持内容可读
  • 使用 .secondary 前景样式表达视觉层次

自定义按钮样式

09:23)创建带悬停效果的自定义按钮:

struct FunFactButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .background(.regularMaterial, in: .rect(cornerRadius: 12))
            .hoverEffect()
            .scaleEffect(configuration.isPressed ? 0.95 : 1)
    }
}

// 使用
Button(action: {
    // perform button action
}) {
    VStack(alignment: .leading, spacing: 12) {
        Text(fact.title)
            .font(.title2)
            .lineLimit(2)
        Text(fact.details)
            .font(.body)
            .lineLimit(4)
        Text("Learn more")
            .font(.caption)
            .foregroundStyle(.secondary)
    }
    .frame(width: 180, alignment: .leading)
}
.buttonStyle(.funFact)

关键点:

  • 自定义 ButtonStyle 需要手动添加 .hoverEffect()
  • 内置控件自动获得悬停效果
  • .hoverEffect() 自动选择适合当前上下文的效果
  • 效果会匹配按钮背景的形状

Volume(3D 窗口)

14:17)创建展示 3D 内容的 Volume:

@main
struct WorldApp: App {
    var body: some Scene {
        WindowGroup {
            Globe()
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 600, height: 600, depth: 600)
    }
}

使用 Model3D 显示 3D 模型:

import SwiftUI
import RealityKit

struct Globe: View {
    @State var rotation = Angle.zero
    
    var body: some View {
        ZStack(alignment: .bottom) {
            Model3D(named: "Earth")
                .rotation3DEffect(rotation, axis: .y)
                .onTapGesture {
                    withAnimation(.bouncy) {
                        rotation.degrees += Double.random(in: 360...720)
                    }
                }
                .padding3D(.front, 200)
            
            GlobeControls()
                .glassBackgroundEffect(in: .capsule)
        }
    }
}

关键点:

  • .windowStyle(.volumetric) 将窗口变为 Volume
  • .defaultSize(width:height:depth:) 设置 3D 尺寸
  • Model3D 异步加载 3D 模型,自动显示占位符
  • rotation3DEffect 在 3D 空间中旋转
  • .padding3D(.front, 200) 在 Z 轴方向添加间距
  • .glassBackgroundEffect 给控件添加玻璃背景

RealityView 和 3D 手势

17:30)使用 RealityView 访问完整 RealityKit 能力:

struct Earth: View {
    @State private var pinLocation: GlobeLocation?
    @State private var rotation = Angle.zero
    @State private var animatingRotation = false
    
    var body: some View {
        RealityView { content in
            if let earth = try? await ModelEntity(named: "Earth") {
                earth.addImageBasedLighting()
                content.add(earth)
            }
        } update: { content, attachments in
            if let pin = attachments.entity(for: "pin") {
                content.add(pin)
                placePin(pin, at: pinLocation)
            }
        } attachments: {
            if let pinLocation {
                GlobePin(pinLocation: pinLocation)
                    .tag("pin")
            }
        }
        .gesture(
            SpatialTapGesture()
                .targetedToAnyEntity()
                .onEnded { value in
                    withAnimation(.bouncy) {
                        rotation.degrees += Double.random(in: 360...720)
                        animatingRotation = true
                    } completion: {
                        animatingRotation = false
                    }
                    pinLocation = lookUpLocation(at: value)
                }
        )
    }
}

关键点:

  • RealityView 的闭包支持 async/await
  • update 闭包在状态变化时调用,用于更新 RealityKit 内容
  • attachments 闭包将 SwiftUI 视图附加到 RealityKit 实体上
  • SpatialTapGesture 提供完整的 3D 点击位置
  • .targetedToAnyEntity() 获取点击的实体和相对位置
  • addImageBasedLighting() 添加基于图像的照明

ImmersiveSpace(沉浸式空间)

21:11)创建完全沉浸的体验:

@main
struct WorldApp: App {
    var body: some Scene {
        // 窗口场景
        WindowGroup {
            ContentView()
        }
        
        // 沉浸式空间
        ImmersiveSpace(id: "solar-system") {
            SolarSystem()
        }
        .immersionStyle(selection: .constant(.full), in: .full)
    }
}

struct ContentView: View {
    @Environment(\.openImmersiveSpace) private var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace
    
    var body: some View {
        Button("View Outer Space") {
            Task {
                await openImmersiveSpace(id: "solar-system")
            }
        }
    }
}

struct SolarSystem: View {
    var body: some View {
        Earth()
        Sun()
        Starfield()
    }
}

struct Starfield: View {
    var body: some View {
        RealityView { content in
            let starfield = await loadStarfield()
            content.add(starfield)
        }
    }
}

关键点:

  • ImmersiveSpace 需要唯一的 id
  • openImmersiveSpace 环境值用于打开空间
  • dismissImmersiveSpace 用于关闭空间
  • .immersionStyle(selection:in:) 设置沉浸样式
  • .full 完全隐藏周围环境
  • .mixed 让虚拟内容与现实共存
  • .progressive 用户可通过数码表冠调节沉浸程度

沉浸样式切换

22:50)支持多种沉浸样式并动态切换:

@main
struct WorldApp: App {
    @State private var selectedStyle: ImmersionStyle = .full
    
    var body: some Scene {
        WindowGroup {
            ContentView(selectedStyle: $selectedStyle)
        }
        
        ImmersiveSpace(id: "solar-system") {
            SolarSystem()
        }
        .immersionStyle(selection: $selectedStyle, in: .full, .mixed, .progressive)
    }
}

关键点:

  • immersionStyleselection 绑定允许运行时切换
  • in 参数列出支持的样式
  • 用户可以通过数码表冠在 .progressive 样式下调节沉浸度

核心启发

1. 为电商应用添加 3D 商品预览

做什么:在商品详情页中添加一个 Volume,让用户可以从各个角度查看商品的 3D 模型。

为什么值得做:3D 预览让用户在购买前更清楚地了解商品外观,减少退货率。Volume 可以和其他应用并列显示,不会打断用户的购物流程。

怎么开始:使用 Model3D 加载商品的 USDZ 模型,用 .windowStyle(.volumetric) 创建 Volume,添加 rotation3DEffectonTapGesture 让用户可以旋转和交互。

2. 构建沉浸式教育应用

做什么:创建一个天文或生物教育应用,在 Window 中展示知识点,通过按钮进入 Full Space 沉浸式体验,让学生身临其境地探索太阳系或细胞结构。

为什么值得做:空间计算让抽象概念变得直观。学生可以”走进”太阳系,从任意角度观察行星运行,这种体验是 2D 屏幕无法提供的。

怎么开始:用 WindowGroup 构建课程导航界面,用 ImmersiveSpace 创建沉浸式场景,通过 openImmersiveSpace 进入。使用 RealityView 加载复杂的 3D 模型,添加 SpatialTapGesture 实现交互式标注。

3. 为设计工具添加 3D 模型查看器

做什么:在平面设计或建筑设计应用中集成 Volume,让用户可以预览导入的 3D 模型,同时继续在 2D 窗口中编辑设计。

为什么值得做:设计师需要同时查看 2D 图纸和 3D 模型。Volume 可以和其他应用窗口并列显示,支持真正的多任务工作流。

怎么开始:使用 RealityView 加载模型,利用 attachments 在 3D 场景上叠加 SwiftUI 控件(如尺寸标注、材质选择面板)。使用 glassBackgroundEffect 让控件融入平台视觉风格。

4. 实现注视感知的交互界面

做什么:利用平台”注视即交互”的特性,让界面元素根据用户的注视位置自动展开更多信息或显示操作提示。

为什么值得做:这是空间计算平台独有的交互方式。TabView 注视展开就是一个很好的例子,同样的模式可以应用到任何信息密度高的界面。

怎么开始:使用 SwiftUI 内置控件(它们自动支持注视交互),为自定义控件添加 .hoverEffect()。结合 ornament 修饰符在窗口边缘放置辅助信息,注视时展开详情。

关联 Session

评论

GitHub Issues · utterances