Highlight
visionOS 26 把 volume 提升为一等公民:可以弹 sheet、可以溢出边界、可以让 3D 物体跟随手势旋转,且只需一个 view modifier。
核心内容
去年 Vision Pro 上市以来,开发者最常踩的坑是:volume 像一个”封闭盒子”。容器内的 3D 内容能渲染,但 alert、sheet、menu 这些日常 UI 在 volume 里没法用,超出窗口边界的几何会被裁掉,3D 物体的旋转手势要自己写一整套。结果是每个 volumetric app 都在重复造轮子,体验还参差不齐。
visionOS 26 把这些痛点逐一拆掉。Edwin 在开场就点明思路:让 SwiftUI 既有的 2D 布局工具自然延伸到 3D。depthAlignment 让 VStack 能沿前后轴对齐,rotation3DLayout 把旋转纳入布局系统,preferredWindowClippingMargins 允许内容溢出边界,Object Manipulation API 用一个 .manipulable() 修饰符替掉自定义手势栈。系统还把 SwiftUI、RealityKit、ARKit 三套坐标系打通,写 RealityKit 的人不再需要绕 RealityView 中转,可以直接给 entity 挂 SwiftUI 手势和 attachment。这些改动加在一起,volume 才真正可以承担起完整的 app 主场景。
详细内容
SwiftUI 布局走入第三维(02:25)
// Layout types back align views by default
struct LandmarkProfile: View {
var body: some View {
VStackLayout().depthAlignment(.front) {
ResizableLandmarkModel()
LandmarkNameCard()
}
}
}
关键点:
VStackLayout()是熟悉的垂直栈,过去只在 X/Y 平面工作。.depthAlignment(.front)把所有子视图沿 Z 轴对齐到同一个前平面;3D 模型可以更深,名片自动贴到模型最前面,可读性显著提升。- 这是布局级修饰符,不需要手工算 offset。
Volume 中的内容溢出(04:22)
// Dynamic Bounds Restrictions
struct ContentView: View, Animatable {
var body: some View {
VStackLayout().depthAlignment(.front) {
// . . .
}
.preferredWindowClippingMargins(.all, 400)
}
}
关键点:
preferredWindowClippingMargins(.all, 400)告诉系统:允许内容向四周各方向溢出 400 点。- 用途是让虚拟物体”探出”窗口,例如山峰冒出 volume、瀑布越过边界,无需为此把 volume 尺寸调大。
- 这是偏好(preferred),系统会在需要时按距离与遮挡情况自行裁剪。
Object Manipulation:一个 modifier 搞定(05:05)
// Apply the manipulable view modifier to each Model3D block per 3D object
struct RockView: View {
var body: some View {
RockLayout {
ForEach(rocks) { rock in
Model3D(named: rock.name, bundle: realityKitContentBundle) {
model in
model.model?
.resizable()
.scaledToFit3D()
}
.manipulable()
}
}
}
}
关键点:
.manipulable()让Model3D自动支持双手抓取、旋转、缩放,与现实物体一致的物理直觉。- 不需要写
DragGesture+RotateGesture3D的组合,也不需要手动处理两手协同。 - 用 RealityKit 的同学走
ManipulationComponent.configureEntity(rock)即可获得相同行为;用 Quick Look 预览的 USDZ 模型则免费获得该能力。
SwiftUI 手势直接挂到 Entity(06:36)
// Gestures on entities
struct GestureExample: View {
@GestureState private var dragMountain: Float = 0
@GestureState private var dragTerrain: Float = 0
var body: some View {
RealityView { content in
let drag1 = GestureComponent(
DragGesture().updating($dragMountain) { value, offset, _ in
offset = Float(value.translation.width)
})
let drag2 = GestureComponent(
DragGesture().updating($dragTerrain) {evalue, offset, _ in
offset = Float(value.translation.width)
})
mountain.components.set(drag1)
terrain.components.set(drag2)
} update: { content in
// . . .
}
}
}
关键点:
GestureComponent把 SwiftUI 的DragGesture直接装到 entity 上,不再需要RealityView当中介转发命中测试。- 每个 entity 可以挂自己独立的手势,互不干扰;以前要写一长串 targetedToEntity 分支判断。
@GestureState仍然走 SwiftUI 的状态管理,过去的写法可以平移过来。
Look to Scroll:抬眼即滚动(33:45)
// SwiftUI
var body: some View {
ScrollView {
HikeDetails()
}
.scrollInputBehavior(.enabled, for: .look)
}
// UIKit
let scrollView: UIScrollView = {
let scroll = UIScrollView()
scroll.lookToScrollAxes = .vertical
return scroll
}()
关键点:
- 在 SwiftUI 中加一行
scrollInputBehavior(.enabled, for: .look),用户视线移到边缘即可触发滚动,无需腾出手。 - UIKit 路线用
lookToScrollAxes,按需指定垂直、水平或两者。 - 阅读类、长内容浏览类 app 是首批受益者;游戏类要谨慎,避免与瞄准操作冲突。
其他重点更新
- Foundation Models 框架(10:53):访问设备端 LLM,支持 guided generation 与 tool calling。
- Spatial Audio Experience API(08:26):每个 window/volume 各自空间化音源,可在 scene 间无缝迁移。
- Environment Occlusion(09:04):通过
EnvironmentBlendingComponent让虚拟物被真实静态物体遮挡。 - Nearby Window Sharing 与共享 ARKit world anchors:同房间多人对同一个 volume 操作;老的 SharePlay 代码无需改动即可获得 nearby 体验,加
isNearbyWithParticipant检查可做更精细的差异化。 - Spatial Browsing 与 HTML
<model>:网页里直接放 USDZ 模型,浏览长文进入空间化阅读模式。 - 企业 API:
Protected Content一个 modifier 禁截屏/录屏/AirPlay;Window Follow Mode 让窗口跟随用户走动;Return to Service 用于设备共享时的安全重置。
核心启发
-
把 volume 当成主舞台,把 window 当成附庸:以前用 window 做主体、volume 装 3D 配件;visionOS 26 之后反过来更合理。volume 现在能弹 sheet、能溢出边界、能空间化音频,主交互完全可以放进去。
- 为什么值得做:让 app 一开始就具备空间感,避免用户在二维窗口里感受不到 Vision Pro 的差异。
- 怎么开始:把首屏 scene 改成
WindowGroup(.volumetric),加上depthAlignment(.front)排版主元素,再用preferredWindowClippingMargins让关键物体探出边界。
-
删掉自己写的 3D 手势栈,全部换成
.manipulable()/ManipulationComponent:手势协同、双手缩放、惯性回弹这些细节苹果已经统一好了,自己实现很难达到系统级一致性。- 为什么值得做:用户在 Quick Look、系统照片、其他第三方 app 中已经形成一致的肌肉记忆,自定义手势会破坏这种心智。
- 怎么开始:把现有的
DragGesture3D+RotateGesture3D组合替换;RealityKit 端在RealityView的 make 阶段对每个可交互 entity 调一次ManipulationComponent.configureEntity(_:)。
-
媒体类 app 优先支持 APMP(Apple Projected Media Profile):Canon、GoPro、Insta360 拍出的 180/360 度与广角素材现在有统一元数据,导入后可直接走系统沉浸式播放管线。
- 为什么值得做:用户从外部相机导入越来越多素材,APMP 让你不必为每种镜头做特例处理。
- 怎么开始:检查上传链路是否保留原始元数据;播放器端使用系统提供的沉浸式 player,让 projection 信息驱动渲染。
-
企业内部 app 默认套上
Protected Content:金融、医疗、设计稿审阅这类视图,截屏与 AirPlay 是合规高风险点。- 为什么值得做:一个 view modifier 就能闭合一类合规问题,零业务逻辑改动。
- 怎么开始:找到展示敏感数据的 view 根节点,加上
Protected Contentmodifier;对外部分享用例评估是否需要 Window Follow Mode 进一步约束。
关联 Session
- Meet SwiftUI spatial layout — 深入讲解 depthAlignment、rotation3DLayout 与 3D 修饰符
- What’s new in RealityKit — ManipulationComponent、MeshInstancesComponent、EnvironmentBlendingComponent 全景
- Set the scene with SwiftUI in visionOS — volume 中 sheet/alert/menu 的展示规则
- Explore video experiences for visionOS — 180/360 度与广角沉浸式视频播放方案
- Learn about Apple Immersive Video technologies — Apple Immersive Video 与空间音频生产链
评论
GitHub Issues · utterances