RealityKit與空間思維模型
visionOS上的SwiftUI讓你獲得一扇懸浮於3D空間中的視窗。RealityKit則讓你獲得整個房間。差異在於思維模型,而思維模型即是架構。
SwiftUI開發者初次接觸visionOS時,往往會建構與iOS相同的形狀:一個內含視圖的WindowGroup。結果是一塊懸浮於空間中的扁平面板。這塊面板對許多應用來說已足夠。但visionOS真正新增的,是面板周圍的整個房間,而房間正是RealityKit的領域。
RealityKit並非3D版的SwiftUI。其架構在形狀上就有所不同,而非僅僅維度的差異。SwiftUI是一棵建立在觀察系統之上、附帶result-builder DSL的值型別視圖樹;框架的職責是根據當前狀態運算下一次的渲染結果。RealityKit則是一個實體—元件—系統(ECS),其場景圖以錨點為根,將虛擬實體綁定到真實世界的參考點上;框架的職責是在使用者與世界移動時,持續維護一個3D場景。同一個Swift專案可能同時使用兩者,而兩者之間的邊界,正是大多數空間設計錯誤發生之處。
本文將梳理空間思維模型。透過五個具體面向,說明此模型與SwiftUI視窗的差異,以及在何種情境下選用何種框架的路由問題。
TL;DR
- RealityKit是一套實體—元件—系統(ECS)。實體是節點;元件是附加於節點上的具型別資料;系統則對具備特定元件組合的實體執行邏輯。
- 錨點將虛擬實體綁定至真實世界的參考點。RealityKit透過
AnchoringComponent.Target(.world、.head、.hand(_:location:)、.plane(...)、.image(...)、.referenceObject(...))暴露錨定目標。在visionOS上,ARKit透過錨點更新串流回報的錨點結構(WorldAnchor、HandAnchor、ImageAnchor、ObjectAnchor、PlaneAnchor)提供底層支援資料。 - 3D場景並非視圖樹。渲染迴圈是連續的,場景圖會隨時間變動,而渲染層則由GPU驅動(底層為Metal),而非基於差異比對。
RealityView是SwiftUI的橋接器。它將RealityKit場景置入SwiftUI視圖樹中;此邊界是單向的(SwiftUI承載RealityKit,反之則不然)。- 路由原則:若使用者想要視窗,出貨SwiftUI。若使用者想要房間,出貨RealityKit。需要兩者兼具的應用,則將
RealityView置入SwiftUI視窗中,並接受兩部分必須明確協調此一現實。
與SwiftUI視窗的五大差異
當你將任何東西放置在面板之外時,形狀就會立刻改變。其中有五項差異至關重要。
1. 實體—元件—系統,而非視圖—主體—狀態
SwiftUI視圖是具有body計算屬性、並由屬性包裝器支援狀態的值型別。1狀態變更時,框架會重新執行body;差異會被送入渲染器。
RealityKit實體則是位於場景圖中的參考型別物件。元件是附加於實體上的具型別結構(ModelComponent、Transform、CollisionComponent、PhysicsBodyComponent,以及你自訂的Component型別)。2系統是遵循System協定的型別;框架每幀會執行一次每個註冊的系統,而System.update(context:)(在結構上通常為mutating)則是系統讀寫符合其查詢條件的實體上之元件之處。
import RealityKit
let cube = Entity()
cube.components.set(ModelComponent(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .blue, isMetallic: false)]
))
cube.components.set(InputTargetComponent())
cube.components.set(CollisionComponent(shapes: [.generateBox(size: [0.1, 0.1, 0.1])]))
這個方塊沒有body。方塊擁有的是一組元件。加入InputTargetComponent與CollisionComponent,方塊才會回應手勢;移除它們,手勢就會直接穿過,落到後方的實體上。加入PhysicsBodyComponent,方塊才會因重力而墜落;移除它,方塊則會懸浮。元件的組合決定了實體的行為。
思維上的轉變:在SwiftUI中,你描述的是螢幕上應該顯示什麼,作為狀態的函數。在RealityKit中,你描述的是實體「是什麼」(它的元件),然後讓系統決定它會發生什麼事。
2. 錨點,而非座標
SwiftUI視圖的座標系統是視窗。位置0,0是左上角;位置以點為單位;視圖的frame就是它的空間。視窗即是宇宙。
RealityKit場景的座標系統則取決於其錨點。錨定層具有兩面。RealityKit的AnchorEntity(由AnchoringComponent.Target驅動)是你將實體放置其上的對象;ARKit的錨點結構則是系統用來讓目標與真實世界保持同步的底層資料。3
在AnchorEntity或AnchoringComponent中可使用的RealityKit錨定目標包括:
.world(transform:):由transform所定義的真實世界空間中的某一點.head:鎖定於使用者的頭部姿勢;實體會跟隨使用者的視線.hand(_:location:):鎖定於特定手部關節(掌心、指尖、手腕等).plane(...):鎖定於偵測到的水平或垂直表面(桌子、牆壁、地板).image(...)/.referenceImage(...):鎖定於環境中辨識到的2D影像.referenceObject(...):鎖定於辨識到的真實世界3D物件
在visionOS上,ARKit透過ARKitSession錨點更新供應器所傳遞的WorldAnchor、HandAnchor、ImageAnchor、ObjectAnchor與PlaneAnchor結構,提供底層錨點資料。(身體追蹤在Apple的身體追蹤介面上僅限iOS/iPadOS;visionOS並未將.body暴露為RealityKit的錨定目標。)
錨點是讓場景「真實化」的關鍵。一個放置於使用者桌面.plane目標上的虛擬西洋棋盤,會在使用者繞著桌子走動時穩穩留在桌上;而一個放置於相對於.head目標的固定座標上的西洋棋盤,則會跟隨使用者的頭部移動,給人一種幻覺感。
思維上的轉變:位置不是一個數字。位置是「錨點是什麼,以及實體相對於錨點的位置在哪裡」。沒有合理錨點的虛擬物件就是一種幻覺;錨點正是告訴使用者「這個物件屬於這個房間」的關鍵。
3. 連續渲染迴圈,而非基於差異比對
SwiftUI在狀態變更時才渲染。框架決定何時需要重新渲染,並運算最小的樹狀結構變更。在兩次渲染之間,畫面是靜態的。
RealityKit則驅動一個基於幀的模擬與渲染迴圈。隨著物理、動畫系統與輸入處理器更新實體的transform與元件值,場景圖會隨時間變動,而渲染器(以Metal為基礎)則於每一幀繪製當前場景。每幀邏輯位於System.update(context:)中;此鉤子是框架邀請你每一刻變動場景的入口。4
思維上的轉變:時間是場景的一部分。SwiftUI視圖body只執行一次是沒問題的;但RealityKit實體必須考慮第N+1幀、N+2幀、N+3幀會發生什麼。自訂System上的update(context:)方法,是你撰寫每幀邏輯的地方;你在update內變動的Component值,則是渲染器在下一幀讀取的資料。
4. RealityView是橋接器,且為單向
SwiftUI視圖組合其他SwiftUI視圖。RealityKit實體組合其他RealityKit實體。兩者之間的邊界是RealityView,一種承載RealityKit場景的SwiftUI視圖型別。5
import SwiftUI
import RealityKit
struct ContentView: View {
var body: some View {
RealityView { content in
// Build the scene
let cube = Entity()
cube.components.set(ModelComponent(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .blue, isMetallic: false)]
))
content.add(cube)
} update: { content in
// Mutate the scene in response to SwiftUI state changes
}
}
}
make閉包在視圖首次出現時執行一次。update閉包則在視圖所依賴的SwiftUI狀態變更時執行。在這兩個閉包中,你都可以透過content參數存取RealityKit場景;你可以加入實體、變動transform、註冊系統。
此邊界是單向的。SwiftUI承載RealityKit。RealityKit並不承載SwiftUI;你無法將SwiftUI視圖「放入」實體中,並期望它如同SwiftUI子視圖在父視圖中渲染那般,作為3D場景的一部分被渲染出來。例外情況是「附件」(在RealityView的attachments:參數中):你宣告具名的SwiftUI視圖,以ViewAttachmentEntity值的形式取得它們,並如同其他實體一般在3D場景中定位與縮放。5附件並非嵌入實體中的SwiftUI視圖;它們是被包裝為實體的2D SwiftUI表面,讓渲染器能將其放置於3D空間中。預設情況下,它們會保持固定方向;若希望它們面向佩戴者,則需在附件實體上附加BillboardComponent。
5. 3D中的手勢截然不同
SwiftUI手勢(.onTapGesture、DragGesture等)在螢幕空間中運作。系統知道手指相對於視圖的位置;框架根據2D命中測試進行調度。
RealityKit手勢則在場景空間中運作。6系統知道使用者「正在看」哪裡(視線射線)、使用者的手在哪裡(手部追蹤關節),以及視線+輕點與哪個實體相交。其調度模型是「使用者注視此實體並做出捏合動作」;這就是輕點的等價物。
實體要接收手勢,必須具備InputTargetComponent與一個定義命中測試幾何形狀的CollisionComponent。沒有InputTargetComponent,實體對手勢系統而言就是不可見的;沒有CollisionComponent,手勢系統就沒有可供命中測試的形狀。兩者必須同時存在。
思維上的轉變:手勢目標不是螢幕區域。手勢目標是明確選擇接收輸入的3D實體。場景的其餘部分則是「使用者可以看穿的裝飾」。
SwiftUI單獨足夠的時候
常見的visionOS應用並不需要RealityKit。以下三種模式中,SwiftUI單獨即是正確答案:
沒有空間內容的視窗形狀應用。冥想計時器、筆記本、設定面板、聊天介面。這些應用是你透過2D介面元素來閱讀或互動的資訊。將其放入WindowGroup並保持扁平,是正確的選擇。visionOS將SwiftUI視窗視為帶有系統外觀的浮動玻璃面板;使用者能獲得舒適的閱讀體驗,而你連一行RealityKit程式碼都不必撰寫。
在空間中組合扁平面板的多視窗應用。程式碼編輯器,將編輯器、終端機、文件分為各自的視窗。使用者希望這些視窗在3D空間中排列(左邊、右邊、後方),但每個視窗本身仍是SwiftUI視圖。3D排列是作業系統的職責;面板本身是扁平的。
文件檢視器、相片庫、影片播放器。使用者透過面板消費的內容。面板是渲染表面;第三維度只是面板在房間中的空間位置。
原則:若「內容」是2D的(文字、影像、影片、控制項),正確的框架就是SwiftUI。第三維度是面板被放置的位置,而非面板內所渲染的東西。
必須使用RealityKit的時候
SwiftUI不夠用的情境:
使用者可以繞著走的3D內容。放在使用者桌上的虛擬物件(模型車、雕塑、建築)。物件具有體積;使用者可以繞著它走動;物件應該與房間正確地產生遮擋關係。正確的框架是RealityKit,並錨定於.plane目標。
回應房間的空間UI。懸浮於真實鍵盤上方的按鈕、附加於真實物件上的註解、沿著真實牆壁鋪設的虛擬捲尺。UI的位置由世界幾何決定,而非視窗的座標空間。RealityKit錨點負責綁定;RealityView內的SwiftUI附件則提供2D介面元素。
連續的空間模擬。鳥群、在地板上滾動的球、流體模擬,任何場景狀態會隨時間演進的事物。連續渲染迴圈是正確的工具;SwiftUI基於差異比對的渲染器要嘛會掉幀,要嘛會耗盡電池。
手部追蹤互動。捏取抓握、雙手縮放、空中繪圖。輸入模型需要ARKit的HandTrackingProvider(配合HandAnchor更新)加上.hand(_:location:)錨定目標;SwiftUI並未暴露此介面。
身體追蹤AR。將使用者的姿勢映射到虛擬角色上、為健身應用追蹤使用者的身體、辨識真實世界物件。擷取與推論發生在ARKit中(RealityKit的低階夥伴);RealityKit負責渲染結果。
原則:若「內容」是3D且存在於房間中的(具體積、有錨定、被模擬,或由手部驅動),正確的框架就是RealityKit。SwiftUI是其外圍裝飾。
組合模式
大多數非平凡的visionOS應用最終都會同時使用兩者。能夠順利出貨的模式是:
- 應用的外圍部分(設定、導覽、清單、表單、檢視面板)位於SwiftUI視窗中。
- 空間場景(使用者操作的體積化內容)位於各自視窗或音量內的
RealityView中。 - 兩者透過SwiftUI狀態溝通。SwiftUI面板中的按鈕切換一個
@State布林值;RealityView的update:閉包讀取該布林值,並變動場景中的實體。 - 需要將RealityKit端的狀態變更回傳至SwiftUI時,則透過
RealityView的make:閉包所註冊的回呼進行(在場景的事件發布器上呼叫subscribe(to:))。7
struct GalleryView: View {
@State private var selectedSculpture: SculptureID?
var body: some View {
HStack {
// SwiftUI side: list of sculptures
List(allSculptures) { sculpture in
Button(sculpture.name) {
selectedSculpture = sculpture.id
}
}
.frame(width: 300)
// RealityKit side: 3D rendering of the selected sculpture
RealityView { content in
// Build initial scene
} update: { content in
guard let id = selectedSculpture else {
content.entities.removeAll()
return
}
// Mutate scene to show the selected sculpture
presentSculpture(id, in: content)
}
}
}
}
這種劃分對「哪個框架負責哪項工作」誠實以對。SwiftUI負責清單、按鈕、版面、狀態。RealityKit負責3D渲染、實體、連續模擬。狀態以單一@State值跨越邊界;兩個框架都不會伸手介入對方領域。
我會在自己的技術堆疊中改採什麼做法
一旦你出貨過一個空間場景,就能輕易識別出三種模式:
錨點優先,實體其次。設計功能時,先決定錨點,再決定幾何形狀。錨定於使用者手部的虛擬樂器,與錨定於桌面.plane目標的同一個樂器,是截然不同的產品。錨點決定使用者與物件的關係;幾何形狀只是實作細節。
用元件,而非子類別。將Entity子類別化以建構領域型別,例如ChessPiece: Entity,聽起來很誘人。組合模式每次都勝過繼承:一枚西洋棋子是一個Entity,具備ChessPieceComponent(自訂資料:顏色、種類、位置)、ModelComponent(3D網格)、InputTargetComponent與CollisionComponent。新行為對應的是新元件,而非新子類別。
用系統處理橫切邏輯。當十個實體都需要相同行為時(重力、碰撞回應、音訊衰減、手勢狀態),撰寫一個對相關元件運作的System。該系統每幀會在所有匹配的實體上執行一次。另一種做法(將邏輯放在每個實體上)會產生n倍幀數的bug,而ECS模式正是為了避免此問題而誕生的。
何時RealityView是錯誤答案
以下幾種情況,選用RealityView是錯誤的選擇:
單一3D影像,無互動。靜態的3D標誌或產品渲染。改用SwiftUI的Model3D視圖。8Model3D是「載入USDZ並顯示」的廉價路徑;RealityView則用於你建構並變動的場景。
具有簡單AR疊加層的iOS應用。ARKit的ARView(較舊的介面)或RealityKit的iOS端ARView整合,在AR體驗作為更大型iOS應用內的一項功能時,通常才是正確的選擇。RealityView是Swift與SwiftUI原生的,能很好地置入SwiftUI中;而當應用其餘部分採用UIKit時,較舊的ARView工作流程有時更為簡單。
面板上的2D繪圖。白板、相片註解工具、扁平形狀編輯器。正確的工具是Canvas(SwiftUI以Metal為基礎的繪圖表面)或MetalView。若你並非在3D空間中建構,使用RealityView只是大材小用。
此模式對於在visionOS 2+上出貨的應用意味著什麼
三項要點。
-
RealityKit與SwiftUI是組合,而非合併。用SwiftUI處理視窗形狀的外圍與2D介面元素;用RealityKit處理房間形狀的3D內容。邊界是
RealityView,且邊界是單向的。 -
思維模型是ECS加上錨點。實體就是其組成元件的總和。錨點決定實體與使用者真實空間的關係。元件與錨點這對組合就是設計單元。
-
渲染迴圈是連續的。時間是場景的一部分。每幀邏輯放入
System.update(context:);每次狀態變更的邏輯放入RealityView.update:。混淆兩個層級(在SwiftUI的body中撰寫每幀邏輯、在System.update中撰寫狀態驅動邏輯)是最常見的架構錯誤。
完整的Apple生態系叢集:用於Apple Intelligence的具型別App Intents;用於跨LLM代理的MCP伺服器;兩者之間的路由問題;用於裝置端LLM與Tool協定的Foundation Models;用於iOS鎖定畫面狀態機的Live Activities;Apple Watch上的watchOS執行時期契約;框架基底的SwiftUI內部結構;視覺層的Liquid Glass模式;跨裝置觸及的多平台出貨。所有內容的中樞位於Apple Ecosystem系列。若需更廣泛的iOS搭配AI代理脈絡,請參閱iOS Agent Development指南。
FAQ
RealityKit是visionOS上SwiftUI的替代品嗎?
不是。RealityKit與SwiftUI是組合關係。SwiftUI處理2D視窗、控制項與外圍;RealityKit處理錨定於真實世界參考點的3D場景。大多數非平凡的visionOS應用會同時使用兩者,並以RealityView作為將RealityKit場景置入SwiftUI視圖樹的橋接器。
我何時該使用RealityView,何時該使用Model3D?
使用Model3D來顯示單一靜態3D資產(USDZ檔案、單一產品渲染)。使用RealityView來隨時間建構或變動3D場景(多個實體、物理、手勢、手部追蹤、錨定內容)。Model3D是廉價路徑;RealityView則是完整的ECS介面。
RealityKit中Entity與Component的差別是什麼?
實體是場景圖中的節點。元件是附加於節點上的具型別資料。ModelComponent賦予實體網格;InputTargetComponent使其具備手勢資格;CollisionComponent定義命中測試幾何;PhysicsBodyComponent使其回應重力。你自訂的Component型別則持有領域資料。行為是組合勝於繼承:實體的行為是其元件的總和。
錨點是什麼?為什麼它們重要?
錨點將虛擬內容綁定至真實世界的參考點:使用者的頭部、手部、偵測到的表面、辨識到的影像、辨識到的物件,或一個持久的世界座標點。錨點決定使用者與實體的關係。位於.plane目標(桌面)上的虛擬物件,在使用者繞行時會穩穩留在原地;位於.head目標上的虛擬物件,則會跟隨使用者的頭部移動。挑選正確的錨點,是空間功能設計上的第一個決策。
RealityKit能在iOS上執行嗎,還是只能在visionOS上?
可以。RealityKit在iOS、iPadOS、macOS與visionOS上都有出貨。由ARKit驅動的AR體驗使用的是RealityKit的iOS介面。visionOS介面額外加入了空間特定的錨點型別(頭部、手部、世界),這些是iOS未暴露的;但核心ECS模式是共通的。
參考資料
-
作者於What SwiftUI Is Made Of中的分析,2026年4月30日,涵蓋值型別視圖樹、result-builder DSL與觀察系統。 ↩
-
Apple Developer,“RealityKit”與“RealityKit Systems”。實體 / 元件 / 系統架構,以及標準元件型別(ModelComponent、Transform、CollisionComponent、PhysicsBodyComponent、InputTargetComponent)。 ↩
-
Apple Developer,“AnchorEntity”、“AnchoringComponent”、“Scene content anchors”,以及ARKit的“Anchor”。RealityKit錨定目標(
.world、.head、.hand(_:location:)、.plane、.image、.referenceObject),以及在visionOS上提供底層資料的ARKit錨點結構(WorldAnchor、HandAnchor、ImageAnchor、ObjectAnchor、PlaneAnchor)。 ↩ -
Apple Developer,“RealityKit Systems”以及WWDC 2024議程“Build a great visionOS app”。RealityKit基於幀的模擬與渲染,以及
System.update(context:)每幀鉤子。 ↩ -
Apple Developer,“RealityView”、“RealityViewAttachments”與“BillboardComponent”。SwiftUI進入RealityKit的橋接、
ViewAttachmentEntity取得模式,以及當2D附件需要面向佩戴者時的可選看板行為。 ↩↩ -
Apple Developer,“Adding 3D content to your app”與“InputTargetComponent”。空間場景中的手勢調度;
InputTargetComponent與CollisionComponent作為輸入啟用配對的角色。 ↩ -
Apple Developer,“Scene”以及基於Combine的
subscribe(to:on:_:)事件發布器,讓RealityKit端的狀態變更得以透過在make:閉包中註冊的回呼回傳至SwiftUI。 ↩ -
Apple Developer,“Model3D”。用於顯示模型資產的SwiftUI視圖;在動用完整RealityKit ECS介面之前的廉價路徑。 ↩