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:10)Model3D 像 Image 一样简单,异步加载和显示 3D 模型。RealityView 提供完整的 RealityKit 能力,支持自定义材质、物理、动画。3D 手势如 SpatialTapGesture 和 RotateGesture3D 让交互更自然。
详细内容
应用入口(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/awaitupdate闭包在状态变化时调用,用于更新 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需要唯一的idopenImmersiveSpace环境值用于打开空间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)
}
}
关键点:
immersionStyle的selection绑定允许运行时切换in参数列出支持的样式- 用户可以通过数码表冠在
.progressive样式下调节沉浸度
核心启发
1. 为电商应用添加 3D 商品预览
做什么:在商品详情页中添加一个 Volume,让用户可以从各个角度查看商品的 3D 模型。
为什么值得做:3D 预览让用户在购买前更清楚地了解商品外观,减少退货率。Volume 可以和其他应用并列显示,不会打断用户的购物流程。
怎么开始:使用 Model3D 加载商品的 USDZ 模型,用 .windowStyle(.volumetric) 创建 Volume,添加 rotation3DEffect 和 onTapGesture 让用户可以旋转和交互。
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
- Create a more responsive camera experience — iOS 17 相机响应性新 API:延迟处理、零快门延迟、响应式拍摄
- Support external cameras in your iPadOS app — 在 iPadOS 应用中使用 Apple Studio Display 等外部相机
- What’s new in the Photos picker — 系统照片选择器的新改进,支持嵌入和连续选择
- What’s new in Background Assets — 后台下载资源,减少用户等待
评论
GitHub Issues · utterances