realitykit-ar
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRealityKit + ARKit
RealityKit + ARKit
Build AR experiences on iOS using RealityKit for rendering and ARKit for world
tracking. Covers , entity management, raycasting, scene
understanding, and gesture-based interactions. Targets Swift 6.2 / iOS 26+.
RealityView在iOS上使用RealityKit进行渲染、ARKit进行世界追踪,构建AR体验。内容涵盖、实体管理、射线投射、场景理解以及基于手势的交互。目标环境为Swift 6.2 / iOS 26+。
RealityViewContents
目录
Setup
设置
Project Configuration
项目配置
- Add to Info.plist
NSCameraUsageDescription - For iOS, RealityKit uses the device camera by default via (iOS 18+, macOS 15+)
RealityViewCameraContent - No additional capabilities required for basic AR on iOS
- 在Info.plist中添加
NSCameraUsageDescription - 在iOS上,RealityKit默认通过使用设备摄像头(iOS 18+、macOS 15+)
RealityViewCameraContent - iOS基础AR功能无需额外配置权限
Device Requirements
设备要求
AR features require devices with an A9 chip or later. Always verify support
before presenting AR UI.
swift
import ARKit
guard ARWorldTrackingConfiguration.isSupported else {
showUnsupportedDeviceMessage()
return
}AR功能需要搭载A9芯片及以上的设备。在展示AR界面前务必验证设备支持性。
swift
import ARKit
guard ARWorldTrackingConfiguration.isSupported else {
showUnsupportedDeviceMessage()
return
}Key Types
核心类型
| Type | Platform | Role |
|---|---|---|
| iOS 18+, visionOS 1+ | SwiftUI view that hosts RealityKit content |
| iOS 18+, macOS 15+ | Content displayed through the device camera |
| All | Base class for all scene objects |
| All | Entity with a visible 3D model |
| All | Tethers entities to a real-world anchor |
| 类型 | 平台 | 作用 |
|---|---|---|
| iOS 18+, visionOS 1+ | 承载RealityKit内容的SwiftUI视图 |
| iOS 18+, macOS 15+ | 通过设备摄像头显示的内容 |
| 全平台 | 所有场景对象的基类 |
| 全平台 | 带有可见3D模型的实体 |
| 全平台 | 将实体绑定到现实世界锚点的实体 |
RealityView Basics
RealityView 基础
RealityViewRealityViewCameraContentswift
import SwiftUI
import RealityKit
struct ARExperienceView: View {
var body: some View {
RealityView { content in
// content is RealityViewCameraContent on iOS
let sphere = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(
color: .blue,
isMetallic: true
)]
)
sphere.position = [0, 0, -0.5] // 50cm in front of camera
content.add(sphere)
}
}
}RealityViewRealityViewCameraContentswift
import SwiftUI
import RealityKit
struct ARExperienceView: View {
var body: some View {
RealityView { content in
// iOS上content为RealityViewCameraContent
let sphere = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(
color: .blue,
isMetallic: true
)]
)
sphere.position = [0, 0, -0.5] // 位于摄像头前方50cm处
content.add(sphere)
}
}
}Make and Update Pattern
创建与更新模式
Use the closure to respond to SwiftUI state changes:
updateswift
struct PlacementView: View {
@State private var modelColor: UIColor = .red
var body: some View {
RealityView { content in
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(
color: .red,
isMetallic: false
)]
)
box.name = "colorBox"
box.position = [0, 0, -0.5]
content.add(box)
} update: { content in
if let box = content.entities.first(
where: { $0.name == "colorBox" }
) as? ModelEntity {
box.model?.materials = [SimpleMaterial(
color: modelColor,
isMetallic: false
)]
}
}
Button("Change Color") {
modelColor = modelColor == .red ? .green : .red
}
}
}使用闭响应用户界面状态变化:
updateswift
struct PlacementView: View {
@State private var modelColor: UIColor = .red
var body: some View {
RealityView { content in
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(
color: .red,
isMetallic: false
)]
)
box.name = "colorBox"
box.position = [0, 0, -0.5]
content.add(box)
} update: { content in
if let box = content.entities.first(
where: { $0.name == "colorBox" }
) as? ModelEntity {
box.model?.materials = [SimpleMaterial(
color: modelColor,
isMetallic: false
)]
}
}
Button("更改颜色") {
modelColor = modelColor == .red ? .green : .red
}
}
}Loading and Creating Entities
加载与创建实体
Loading from USDZ Files
从USDZ文件加载
Load 3D models asynchronously to avoid blocking the main thread:
swift
RealityView { content in
if let robot = try? await ModelEntity(named: "robot") {
robot.position = [0, -0.2, -0.8]
robot.scale = [0.01, 0.01, 0.01]
content.add(robot)
}
}异步加载3D模型以避免阻塞主线程:
swift
RealityView { content in
if let robot = try? await ModelEntity(named: "robot") {
robot.position = [0, -0.2, -0.8]
robot.scale = [0.01, 0.01, 0.01]
content.add(robot)
}
}Programmatic Mesh Generation
程序化生成网格
swift
// Box
let box = ModelEntity(
mesh: .generateBox(size: [0.1, 0.2, 0.1], cornerRadius: 0.005),
materials: [SimpleMaterial(color: .gray, isMetallic: true)]
)
// Sphere
let sphere = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .blue, roughness: 0.2, isMetallic: true)]
)
// Plane
let plane = ModelEntity(
mesh: .generatePlane(width: 0.3, depth: 0.3),
materials: [SimpleMaterial(color: .green, isMetallic: false)]
)swift
// 立方体
let box = ModelEntity(
mesh: .generateBox(size: [0.1, 0.2, 0.1], cornerRadius: 0.005),
materials: [SimpleMaterial(color: .gray, isMetallic: true)]
)
// 球体
let sphere = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .blue, roughness: 0.2, isMetallic: true)]
)
// 平面
let plane = ModelEntity(
mesh: .generatePlane(width: 0.3, depth: 0.3),
materials: [SimpleMaterial(color: .green, isMetallic: false)]
)Adding Components
添加组件
Entities use an ECS (Entity Component System) architecture. Add components
to give entities behavior:
swift
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .red, isMetallic: false)]
)
// Make it respond to physics
box.components.set(PhysicsBodyComponent(
massProperties: .default,
material: .default,
mode: .dynamic
))
// Add collision shape for interaction
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
// Enable input targeting for gestures
box.components.set(InputTargetComponent())实体采用ECS(实体组件系统)架构。通过添加组件为实体赋予行为:
swift
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .red, isMetallic: false)]
)
// 启用物理特性
box.components.set(PhysicsBodyComponent(
massProperties: .default,
material: .default,
mode: .dynamic
))
// 添加碰撞形状以支持交互
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
// 启用输入目标以支持手势
box.components.set(InputTargetComponent())Anchoring and Placement
锚定与放置
AnchorEntity
AnchorEntity
Use to anchor content to detected surfaces or world positions:
AnchorEntityswift
RealityView { content in
// Anchor to a horizontal surface
let floorAnchor = AnchorEntity(.plane(
.horizontal,
classification: .floor,
minimumBounds: [0.2, 0.2]
))
let model = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .orange, isMetallic: false)]
)
floorAnchor.addChild(model)
content.add(floorAnchor)
}使用将内容锚定到检测到的表面或世界位置:
AnchorEntityswift
RealityView { content in
// 锚定到水平表面
let floorAnchor = AnchorEntity(.plane(
.horizontal,
classification: .floor,
minimumBounds: [0.2, 0.2]
))
let model = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .orange, isMetallic: false)]
)
floorAnchor.addChild(model)
content.add(floorAnchor)
}Anchor Targets
锚定目标
| Target | Description |
|---|---|
| Horizontal surfaces (floors, tables) |
| Vertical surfaces (walls) |
| Any detected plane |
| Fixed world-space position |
| 目标 | 描述 |
|---|---|
| 水平表面(地面、桌面) |
| 垂直表面(墙面) |
| 任意检测到的平面 |
| 固定的世界空间位置 |
Raycasting
射线投射
Use to convert between SwiftUI view coordinates
and RealityKit world space. Pair with to place objects
where the user taps on a detected surface.
RealityViewCameraContentSpatialTapGesture使用在SwiftUI视图坐标与RealityKit世界空间之间进行转换。结合,可在用户点击检测到的表面处放置对象。
RealityViewCameraContentSpatialTapGestureGestures and Interaction
手势与交互
Drag Gesture on Entities
实体拖拽手势
swift
struct DraggableARView: View {
var body: some View {
RealityView { content in
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .blue, isMetallic: true)]
)
box.position = [0, 0, -0.5]
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
box.components.set(InputTargetComponent())
box.name = "draggable"
content.add(box)
}
.gesture(
DragGesture()
.targetedToAnyEntity()
.onChanged { value in
let entity = value.entity
guard let parent = entity.parent else { return }
entity.position = value.convert(
value.location3D,
from: .local,
to: parent
)
}
)
}
}swift
struct DraggableARView: View {
var body: some View {
RealityView { content in
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .blue, isMetallic: true)]
)
box.position = [0, 0, -0.5]
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
box.components.set(InputTargetComponent())
box.name = "draggable"
content.add(box)
}
.gesture(
DragGesture()
.targetedToAnyEntity()
.onChanged { value in
let entity = value.entity
guard let parent = entity.parent else { return }
entity.position = value.convert(
value.location3D,
from: .local,
to: parent
)
}
)
}
}Tap to Select
点击选择
swift
.gesture(
SpatialTapGesture()
.targetedToAnyEntity()
.onEnded { value in
let tappedEntity = value.entity
highlightEntity(tappedEntity)
}
)swift
.gesture(
SpatialTapGesture()
.targetedToAnyEntity()
.onEnded { value in
let tappedEntity = value.entity
highlightEntity(tappedEntity)
}
)Scene Understanding
场景理解
Per-Frame Updates
逐帧更新
Subscribe to scene update events for continuous processing:
swift
RealityView { content in
let entity = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .yellow, isMetallic: false)]
)
entity.position = [0, 0, -0.5]
content.add(entity)
_ = content.subscribe(to: SceneEvents.Update.self) { event in
let time = Float(event.deltaTime)
entity.position.y += sin(Float(Date().timeIntervalSince1970)) * time * 0.1
}
}订阅场景更新事件以进行持续处理:
swift
RealityView { content in
let entity = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .yellow, isMetallic: false)]
)
entity.position = [0, 0, -0.5]
content.add(entity)
_ = content.subscribe(to: SceneEvents.Update.self) { event in
let time = Float(event.deltaTime)
entity.position.y += sin(Float(Date().timeIntervalSince1970)) * time * 0.1
}
}visionOS Note
visionOS 说明
On visionOS, ARKit provides a different API surface with ,
, and . These visionOS-specific
types are not available on iOS. On iOS, RealityKit handles world tracking
automatically through .
ARKitSessionWorldTrackingProviderPlaneDetectionProviderRealityViewCameraContent在visionOS上,ARKit提供了不同的API,包括、和。这些visionOS专属类型在iOS上不可用。在iOS上,RealityKit通过自动处理世界追踪。
ARKitSessionWorldTrackingProviderPlaneDetectionProviderRealityViewCameraContentCommon Mistakes
常见错误
DON'T: Skip AR capability checks
错误做法:跳过AR兼容性检查
Not all devices support AR. Showing a black camera view with no feedback
confuses users.
swift
// WRONG -- no device check
struct MyARView: View {
var body: some View {
RealityView { content in
// Fails silently on unsupported devices
}
}
}
// CORRECT -- check support and show fallback
struct MyARView: View {
var body: some View {
if ARWorldTrackingConfiguration.isSupported {
RealityView { content in
// AR content
}
} else {
ContentUnavailableView(
"AR Not Supported",
systemImage: "arkit",
description: Text("This device does not support AR.")
)
}
}
}并非所有设备都支持AR。直接显示黑屏且无提示会使用户困惑。
swift
// 错误示例 -- 未做设备检查
struct MyARView: View {
var body: some View {
RealityView { content in
// 在不支持的设备上会静默失败
}
}
}
// 正确示例 -- 检查支持性并提供回退界面
struct MyARView: View {
var body: some View {
if ARWorldTrackingConfiguration.isSupported {
RealityView { content in
// AR内容
}
} else {
ContentUnavailableView(
"AR不支持",
systemImage: "arkit",
description: Text("该设备不支持AR功能。")
)
}
}
}DON'T: Load heavy models synchronously
错误做法:同步加载大型模型
Loading large USDZ files on the main thread causes frame drops and hangs.
The closure of is -- use it.
makeRealityViewasyncswift
// WRONG -- synchronous load blocks the main thread
RealityView { content in
let model = try! Entity.load(named: "large-scene")
content.add(model)
}
// CORRECT -- async load
RealityView { content in
if let model = try? await ModelEntity(named: "large-scene") {
content.add(model)
}
}在主线程加载大型USDZ文件会导致帧丢卡顿。的闭包支持,请使用异步加载。
RealityViewmakeasyncswift
// 错误示例 -- 同步加载阻塞主线程
RealityView { content in
let model = try! Entity.load(named: "large-scene")
content.add(model)
}
// 正确示例 -- 异步加载
RealityView { content in
if let model = try? await ModelEntity(named: "large-scene") {
content.add(model)
}
}DON'T: Forget collision and input target components for interactive entities
错误做法:交互式实体缺少碰撞与输入组件
Gestures only work on entities that have both and
. Without them, taps and drags pass through.
CollisionComponentInputTargetComponentswift
// WRONG -- entity ignores gestures
let box = ModelEntity(mesh: .generateBox(size: 0.1))
content.add(box)
// CORRECT -- add collision and input components
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .red, isMetallic: false)]
)
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
box.components.set(InputTargetComponent())
content.add(box)只有同时添加和的实体才能响应手势。缺少这些组件时,点击和拖拽操作会无效。
CollisionComponentInputTargetComponentswift
// 错误示例 -- 实体无法响应手势
let box = ModelEntity(mesh: .generateBox(size: 0.1))
content.add(box)
// 正确示例 -- 添加碰撞与输入组件
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .red, isMetallic: false)]
)
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
box.components.set(InputTargetComponent())
content.add(box)DON'T: Create new entities in the update closure
错误做法:在update闭包中创建新实体
The closure runs on every SwiftUI state change. Creating entities
there duplicates content on each render pass.
updateswift
// WRONG -- duplicates entities on every state change
RealityView { content in
// empty
} update: { content in
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.05))
content.add(sphere) // Added again on every update
}
// CORRECT -- create in make, modify in update
RealityView { content in
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.05))
sphere.name = "mySphere"
content.add(sphere)
} update: { content in
if let sphere = content.entities.first(
where: { $0.name == "mySphere" }
) as? ModelEntity {
// Modify existing entity
sphere.position.y = newYPosition
}
}updateswift
// 错误示例 -- 状态变化时重复创建实体
RealityView { content in
// 空实现
} update: { content in
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.05))
content.add(sphere) // 每次更新都会重复添加
}
// 正确示例 -- 在make中创建,在update中修改
RealityView { content in
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.05))
sphere.name = "mySphere"
content.add(sphere)
} update: { content in
if let sphere = content.entities.first(
where: { $0.name == "mySphere" }
) as? ModelEntity {
// 修改现有实体
sphere.position.y = newYPosition
}
}DON'T: Ignore camera permission
错误做法:忽略相机权限
RealityKit on iOS needs camera access. If the user denies permission, the
view shows a black screen with no explanation.
swift
// WRONG -- no permission handling
RealityView { content in
// Black screen if camera denied
}
// CORRECT -- check and request permission
struct ARContainerView: View {
@State private var cameraAuthorized = false
var body: some View {
Group {
if cameraAuthorized {
RealityView { content in
// AR content
}
} else {
ContentUnavailableView(
"Camera Access Required",
systemImage: "camera.fill",
description: Text("Enable camera in Settings to use AR.")
)
}
}
.task {
let status = AVCaptureDevice.authorizationStatus(for: .video)
if status == .authorized {
cameraAuthorized = true
} else if status == .notDetermined {
cameraAuthorized = await AVCaptureDevice
.requestAccess(for: .video)
}
}
}
}iOS上的RealityKit需要相机访问权限。如果用户拒绝权限,界面会显示黑屏且无说明。
swift
// 错误示例 -- 未处理权限
RealityView { content in
// 相机权限被拒绝时显示黑屏
}
// 正确示例 -- 检查并请求权限
struct ARContainerView: View {
@State private var cameraAuthorized = false
var body: some View {
Group {
if cameraAuthorized {
RealityView { content in
// AR内容
}
} else {
ContentUnavailableView(
"需要相机权限",
systemImage: "camera.fill",
description: Text("请在设置中启用相机权限以使用AR功能。")
)
}
}
.task {
let status = AVCaptureDevice.authorizationStatus(for: .video)
if status == .authorized {
cameraAuthorized = true
} else if status == .notDetermined {
cameraAuthorized = await AVCaptureDevice
.requestAccess(for: .video)
}
}
}
}Review Checklist
检查清单
- set in Info.plist
NSCameraUsageDescription - AR device capability checked before presenting AR views
- Camera permission requested and denial handled with a fallback UI
- 3D models loaded asynchronously in the closure
make - Entities created in , modified in
make(not created inupdate)update - Interactive entities have both and
CollisionComponentInputTargetComponent - Collision shapes match the visual size of the entity
- subscriptions used for per-frame logic (not SwiftUI timers)
SceneEvents.Update - Large scenes use async loading, not
ModelEntity(named:)Entity.load(named:) - Anchor entities target appropriate surface types for the use case
- Entity names set for lookup in the closure
update
- Info.plist中已设置
NSCameraUsageDescription - 展示AR界面之前已检查设备AR支持性
- 已处理相机权限,拒绝时提供回退UI
- 3D模型已在闭包中异步加载
make - 实体在中创建,仅在
make中修改(不在update中创建)update - 交互式实体已同时添加和
CollisionComponentInputTargetComponent - 碰撞形状与实体视觉尺寸匹配
- 使用订阅实现逐帧逻辑(而非SwiftUI定时器)
SceneEvents.Update - 大型场景使用异步加载,而非
ModelEntity(named:)Entity.load(named:) - 锚定实体已根据使用场景选择合适的表面类型
- 已为实体设置名称,以便在闭包中查找
update
References
参考资料
- Extended patterns (physics, animations, lighting, ECS):
references/realitykit-patterns.md - RealityKit framework
- RealityView
- RealityViewCameraContent
- Entity
- ModelEntity
- AnchorEntity
- ARKit framework
- ARKit in iOS
- ARWorldTrackingConfiguration
- Loading entities from a file
- 扩展模式(物理、动画、光照、ECS):
references/realitykit-patterns.md - RealityKit框架
- RealityView
- RealityViewCameraContent
- Entity
- ModelEntity
- AnchorEntity
- ARKit框架
- iOS中的ARKit
- ARWorldTrackingConfiguration
- 从文件加载实体