← 所有文章

visionOS 空間模式:超越視窗

大多數在 visionOS 上推出的應用程式,都是透過 Apple 的「Designed for iPad」相容性路徑進入這個平台:既有的 iPad 二進位檔案以漂浮在 3D 空間中的扁平面板形式執行,開發者只需勾選一個選項,而非建構真正的 visionOS 原生體驗。這條路徑對使用者來說沒問題(應用程式可以運作),但卻辜負了平台。visionOS 的原生表面為開發者提供了三種呈現方式(Windows、Volumes 和 Immersive Spaces),加上結構化的 UI 基礎元素(Ornaments、Attachments),這些都是 iPad SDK 所沒有的。採用這些元素的應用程式才有原生感;不採用的則被視為「Vision 上的 iPad 應用」。

本文對照 Apple 的文件來介紹空間詞彙。框架是「平台實際提供給 SwiftUI 應用程式什麼」,而非 visionOS 入門介紹。本系列的 RealityKit 與空間心智模型 一文涵蓋 3D 內容層;本文則涵蓋包含這些內容的 SwiftUI 表面層。

TL;DR

  • visionOS 應用程式由三種場景類型組成:WindowGroup(Windows)、加上 .windowStyle(.volumetric)WindowGroup(Volumes),以及 ImmersiveSpace(Immersive Spaces)1
  • Window 是 2D 平面;Volume 是 3D 有界區域;Immersive Space 環繞使用者。每一種都有不同的規則:Volumes 在建立後尺寸不可變更、Immersive Spaces 需要明確開啟與關閉、Windows 的行為最接近 iPad。
  • 沉浸感分為三種樣式:.mixed(內容與房間共存)、.full(房間被虛擬環境取代)、.progressive(保有周邊感知的中間地帶)2
  • Ornaments 是與 Window 平行的 UI 平面,在 z 軸上略微靠前。它們是 visionOS 實作工具列與分頁列的方式3。Attachments 將 SwiftUI 視圖嵌入 RealityView 的 3D 內容中,是扁平 UI 與空間幾何之間的橋樑。
  • 「面板應用」反模式:將 iPad UI 作為 Window 推出,而沒有採用 Volume、Space 或 Ornament。使用者可以使用該應用程式,但平台的真正價值未被發揮。

三種場景類型

visionOS 應用程式的 App 主體由三類場景組成。每一類都有獨特的使用者心智模型。

Windows:2D 平面

WindowGroup 預設會產生帶有 visionOS 玻璃外框的 2D Window。Window 被定位在空間中(系統會將其放置在使用者注視方向的前方),使用者可透過標準的系統手勢移動或調整大小。從 SwiftUI 的角度來看,Window 是 macOS 視窗在 visionOS 上的對應物:具備深度感知玻璃材質的扁平內容表面。

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

預設的 Window 在內容周圍有玻璃材質。希望表面完全透明的應用程式可使用 .windowStyle(.plain)

WindowGroup {
    ContentView()
}
.windowStyle(.plain)

Plain 樣式的 Windows 會失去系統玻璃外框。當內容自身提供視覺容器時可使用此樣式;否則預設值才是正確選擇。

Volumes:3D 有界區域

Volume 是包含深度感知內容的 3D 區域(一個模型、一個包含多個物件的場景、一個受益於第三軸的 UI)。Volume 場景同樣是 WindowGroup,只是樣式不同:

WindowGroup(id: "globe") {
    GlobeView()
}
.windowStyle(.volumetric)
.defaultSize(width: 0.6, height: 0.6, depth: 0.6, in: .meters)

.defaultSize(width:height:depth:in:) 修飾符以實際單位(公尺)指定 Volume 的邊界。預設情況下,邊界在開啟時就固定了,使用者只能移動 Volume,無法調整大小。visionOS 2+ 透過 .windowResizability(.contentSize) 與相關的 API 增加了選擇性路徑,供希望讓使用者可調整 Volume 大小的應用程式使用;固定大小的預設值仍是最常見的情況。這意味著:必須謹慎選擇預設大小,因為大多數 Volumes 在開發者明確選擇加入之前都無法調整大小。

Volumes 適合的應用是那些將空間邊界視為體驗一部分的應用程式:使用者可以繞著走的虛擬雕塑、釘在真實牆面上的捲尺、具有深度錯落目標的健身場景。只是想要更寬畫布的應用程式無法從 Volume 獲益;較大的 Window 才是正確答案。

Immersive Spaces:環繞

ImmersiveSpace 是佔據使用者周圍環境的場景。與 Window 或 Volume 不同(兩者都會在 Shared Space 中與其他應用程式並存可見),Immersive Space 接管使用者的環境,並阻擋其他應用程式視窗的同時使用。

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }

        ImmersiveSpace(id: "training") {
            TrainingScene()
        }
        .immersionStyle(selection: .constant(.mixed), in: .mixed, .progressive, .full)
    }
}

.immersionStyle(...) 修飾符選擇體驗層級:

  • .mixed 虛擬內容與真實房間並存出現。適用於使用者同時受益於兩種情境的應用程式。
  • .progressive 部分沉浸,使用 Digital Crown 來增減程度。使用者保留對房間的周邊感知,同時中央視野是虛擬的。
  • .full 房間被虛擬環境取代。適用於完全沉浸式體驗(冥想、訓練模擬、遊戲)。

開啟 Immersive Space 是明確的動作。應用程式以該空間的 id 呼叫 @Environment(\.openImmersiveSpace);系統處理過渡動畫以及任何衝突空間的關閉:

@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace

Button("Start Session") {
    Task {
        await openImmersiveSpace(id: "training")
    }
}

每個應用程式同一時間只能啟用一個 Immersive Space。Spaces 之間的切換(例如從 .mixed.full)需要明確關閉舊的 Space 並開啟新的。

Ornaments:圍繞 Window 的 UI 平面

Ornaments 是附加於 Window 邊緣的 SwiftUI 視圖,在 z 軸上略微位於 Window 平面之前。它們是 visionOS 實作工具列、分頁列和輔助控制項的方式。系統處處使用 ornaments:TV 中的播放控制、Music 中的分段控制、Mail 中的工具列。

ContentView()
    .ornament(
        attachmentAnchor: .scene(.bottom),
        contentAlignment: .center
    ) {
        HStack {
            Button("Previous", systemImage: "backward.fill") { ... }
            Button("Play", systemImage: "play.fill") { ... }
            Button("Next", systemImage: "forward.fill") { ... }
        }
        .padding()
        .glassBackgroundEffect()
    }

attachmentAnchor: 參數指定 ornament 相對於 Window 的位置:.scene(.top).scene(.bottom).scene(.leading).scene(.trailing)。Ornament 的視覺處理由開發者負責;.glassBackgroundEffect() 會產生與 Window 外框相符的 visionOS 原生玻璃材質。

Ornaments 解決了 visionOS 上一個真實的問題:將控制項放在 Window 內會擠壓內容;放在獨立的 Window 中則迫使使用者重新瞄準視線。Ornament 漂浮在使用者的周邊視野中,可被視線瞄準,但不會與主要內容爭奪中央視野。

RealityView Attachments:3D 空間中的 SwiftUI

當應用程式需要在 3D 場景中使用 SwiftUI 視圖時(3D 模型上的標籤、漂浮在虛擬物件附近的按鈕、釘在真實世界表面的測量讀數),橋樑就是 RealityView 的 attachments 機制。

RealityView { content, attachments in
    let model = ModelEntity(...)
    content.add(model)

    if let label = attachments.entity(for: "label") {
        label.position = [0, 0.5, 0]
        model.addChild(label)
    }
} attachments: {
    Attachment(id: "label") {
        Text("Vintage Globe, 1872")
            .padding()
            .glassBackgroundEffect()
    }
}

attachments: 閉包以穩定的識別碼宣告 SwiftUI 視圖。在主要的 RealityView 閉包內,attachments.entity(for:) 將該視圖取回為可在場景座標空間中定位的 3D Entity。該視圖參與 SwiftUI 的更新週期(狀態變更會重繪視圖),同時在 3D 場景中以帶紋理的平面呈現。

這個機制適用於任何世界內 UI:跟隨移動物件的標籤、測量註記、情境式按鈕。SwiftUI 視圖的撰寫方式不變;3D 定位則發生在 RealityView 層。

「面板應用」反模式

最常見的 visionOS 推出錯誤就是面板應用:透過「Designed for iPad」相容性進入 visionOS 的 iPad 應用程式,以單一 Window 推出,沒有 Volume、沒有 Immersive Space、也沒有 Ornaments。應用程式可以運作,但並未真正贏得平台。

判斷一個應用程式是否為面板應用的三個訊號:

單一 Window 場景。 沒有 .windowStyle(.volumetric),也沒有宣告 ImmersiveSpace。應用程式就是一個扁平表面,僅此而已。

未採用 ornaments。 應用程式的分頁列存在於 Window 內容裡,而非外部。結果是在相同內容密度下比 visionOS 原生應用更為擁擠。

沒有空間獨有的功能。 應用程式完全沒有運用第三軸:沒有 Volume 中的 3D 模型、沒有 Space 中的環境場景、沒有透過 attachments 進行 z 軸定位的 UI。應用程式做的事情和在 iPad 上一模一樣,只是漂浮著。

面板應用並非失敗;對於不受益於空間運算的內容類別(聊天應用、筆記應用、設定工具),它們是正確的選擇。失敗模式是推出面板應用 並聲稱它擁有 visionOS 原生權威。本系列的 Apple 平台矩陣 一文主張平台納入是產品決策;對 visionOS 而言,這個決策是「這個應用程式應該贏得空間表面,還是面板就足夠了?」

常見失誤

三種會產生糟糕 visionOS UX 的模式:

實際上是有深度填充的 2D 內容的 Volumes。 一個填滿 Volume 但內部呈現扁平面板的「3D」UI 浪費了空間。Volumes 是給 3D 內容的;扁平內容應該屬於 Window。

與使用情境衝突的沉浸樣式。 只推出 .full 沉浸的冥想應用程式,會在短時間使用時將使用者強制帶離環境。只推出 .mixed 沉浸的訓練應用程式,對於完全專注的訓練又不夠深入。沉浸樣式應該與使用者的實際使用情境相符。

與內容爭奪注意力的 Ornaments。 Ornaments 在設計上就是周邊性的。要求中央注意力的 ornament(閃爍的顏色、動態的動作)違背了其用途。Ornaments 應用於穩定、可一瞥而知的控制項。

這個模式對 visionOS 應用程式的意義

三個重點。

  1. 依使用者的心智模型選擇場景類型,而非依方便程度。 扁平的項目清單是 Window。使用者檢視的 3D 模型是 Volume。環繞的環境是 Immersive Space。在一個應用程式中混用它們(一個 Window 搭配按需開啟的 Volume、一個可從 Window 按鈕進入的 Immersive Space)才是 visionOS 原生模式。

  2. 為工具列與輔助 UI 採用 ornaments。 Ornaments 是 visionOS 用來表達「此 UI 是輔助性的」的方式;將工具列放在 Window 內容裡會被解讀為「Vision 上的 iPad」。整合的工程量很小,但視覺差異很大。

  3. 在 RealityView 中為世界內 UI 使用 attachments。 3D 物件上的標籤、虛擬內容附近的按鈕、情境讀數。SwiftUI 與 3D 空間之間的橋樑已經解決;失敗模式是不使用它,最後落入臨時拼湊的 3D 文字渲染。

完整的 Apple 生態系列:型別化的 App IntentsMCP 伺服器路由問題Foundation Models執行階段與工具 LLM 之區分三個表面單一真實來源模式兩個 MCP 伺服器Apple 開發專用的 hooksLive ActivitieswatchOS 執行階段SwiftUI 內部結構RealityKit 的空間心智模型SwiftData 結構紀律Liquid Glass 模式多平台發行平台矩陣Vision 框架Symbol EffectsCore ML 推論Writing Tools APISwift TestingPrivacy Manifest輔助使用作為平台SF Pro 字型我拒絕撰寫的內容。中心點在 Apple 生態系列。如需更廣泛的 iOS 與 AI agents 情境,請參閱 iOS Agent 開發指南

FAQ

Volume 與 Immersive Space 之間有什麼區別?

Volume 是位於 Shared Space 中、與其他應用程式共存的有界 3D 區域。使用者可以繞著它走動,系統會為它定框,其他應用程式的 Windows 仍然可見。Immersive Space 環繞使用者、接管環境,並阻止其他應用程式的同時使用。Volumes 用於「看看這個 3D 物件」;Spaces 用於「身處這個環境中」。

我可以同時開啟多個 Volumes 嗎?

可以。多個套用 .volumetricWindowGroup 場景可以同時開啟,每個都有自己的大小與內容。系統會在空間中獨立定位它們。

我可以同時開啟多個 Immersive Spaces 嗎?

不行。每個應用程式同一時間只能啟用一個 Immersive Space。在 Spaces 之間切換需要透過 @Environment(\.openImmersiveSpace)@Environment(\.dismissImmersiveSpace) 明確關閉目前的並開啟新的。

Volume 的尺寸真的不可變更嗎?

Volume 的邊界預設在開啟時就固定了;visionOS HIG 的觀點是 Volumes 代表具有刻意邊界的特定 3D 內容,任意的使用者調整大小會扭曲內容的預期比例。visionOS 2+ 透過 .windowResizability(.contentSize) 與相關的 API 為可調整大小的 Volumes 增加了開發者選擇加入機制,因此需要使用者可調整空間容器的應用程式可以提出請求。大多數 Volumes 仍以固定預設值推出,HIG 也持續為具有特定比例的內容(虛擬雕塑、實際尺寸的模型)推薦此預設值。

如何在 visionOS Window 中加入分頁列?

使用 Window 內的 TabView 來實作內容內分頁(iPad 風格的模式),或使用帶有自訂按鈕列的 ornament 來實作 visionOS 原生的周邊分頁 UI。Ornament 路徑是 Apple 自家應用程式(Music、Mail)所使用的方式,也是讓 visionOS 使用者感覺最原生的方式。

RealityView attachments 可以與手部追蹤互動嗎?

可以。Attachments 一旦定位後就是 3D 實體,與其他 RealityKit 實體一樣參與相同的手勢與命中測試系統。透過 SwiftUI 標準的手勢修飾符即可附加點擊、拖曳和懸停手勢;本系列的 RealityKit 文章 涵蓋手部追蹤的整合模式。

參考資料


  1. Apple Developer:Meet SwiftUI for spatial computing(WWDC 2023 session 10109)。介紹 WindowGroup、volumetric WindowGroup 與 ImmersiveSpace 為 visionOS 的三種場景類型。 

  2. Apple Developer Documentation:ImmersionStyle。三種沉浸樣式(.mixed.progressive.full)以及 .immersionStyle(selection:in:) 修飾符 API。 

  3. Apple Developer Documentation:ornament(visibility:attachmentAnchor:contentAlignment:ornament:)。將 ornament UI 平面以指定錨點加到 Window 上的 SwiftUI 視圖修飾符。 

  4. Apple Developer:Go beyond the window with SwiftUI(WWDC 2023 session 10111)。涵蓋 Volumes、Immersive Spaces 以及在 visionOS 上超越扁平面板 UI 的模式。 

  5. Apple Developer Documentation:Creating an immersive space in visionOS with SwiftUI。定義與開啟 immersive spaces 的端對端指南。 

相關文章

RealityKit And The Spatial Mental Model

RealityKit is an entity-component-system, not SwiftUI in 3D. Anchors place entities in real space. Five ways the model d…

16 分鐘閱讀

Liquid Glass in SwiftUI: Three Patterns From Shipping Return on iOS 26

Apple's Liquid Glass is a one-line SwiftUI API. Three patterns from Return go beyond .glassEffect(): glass on text via C…

19 分鐘閱讀

The Cleanup Layer Is the Real AI Agent Market

Charlie Labs pivoted from building agents to cleaning up after them. The AI agent market is moving from generation to pr…

15 分鐘閱讀