為您的visionOS應(yīng)用程序添加深度和維度,并發(fā)現(xiàn)如何將應(yīng)用程序的內(nèi)容整合到人們的環(huán)境中。
概述
帶有立體顯示的設(shè)備讓人們以更真實(shí)的方式體驗(yàn)3D內(nèi)容。內(nèi)容似乎有真正的深度,人們可以從不同的角度觀看它,讓它看起來(lái)像是在他們面前。
在為visionOS構(gòu)建應(yīng)用程序時(shí),請(qǐng)考慮如何為應(yīng)用程序的界面增加深度。該系統(tǒng)提供了多種方式來(lái)顯示3D內(nèi)容,包括在現(xiàn)有窗口、卷和沉浸式空間中。選擇最適合您的應(yīng)用程序和您提供的內(nèi)容的選項(xiàng)。



為傳統(tǒng)的2D窗口添加深度
Windows是應(yīng)用程序界面的重要組成部分。使用visionOS,應(yīng)用程序會(huì)自動(dòng)獲取具有visionOS外觀和感覺的材料,具有完全可調(diào)整大小的窗口,其間距為眼睛和手的輸入進(jìn)行了調(diào)整,并可以訪問自定義控件的突出顯示調(diào)整。

根據(jù)需要將深度效果整合到自定義視圖中,并使用3D布局選項(xiàng)在窗口中排列視圖。
? Apply a shadow(color:radius:x:y:) or visualEffect(_:) modifier to the view.
? Lift or highlight the view when someone looks at it using a hoverEffect(_:isEnabled:) modifier.
? Lay out views using a ZStack.
? Animate view-related changes with transform3DEffect(_:).
? Rotate the view using a rotation3DEffect(_:axis:anchor:anchorZ:perspective:) modifier.
除了提供更深入的2D視圖外,您還可以將靜態(tài)3D模型添加到2D窗口中。Model3D視圖加載USDZ文件或其他資產(chǎn)類型,并在您的窗口中按其內(nèi)在大小顯示。在應(yīng)用程序中已經(jīng)擁有模型數(shù)據(jù)的地方使用它,或者可以從網(wǎng)絡(luò)下載它。例如,購(gòu)物應(yīng)用程序可能會(huì)使用這種類型的視圖來(lái)顯示產(chǎn)品的3D版本。
使用RealityKit顯示動(dòng)態(tài)3D場(chǎng)景
RealityKit是蘋果用于構(gòu)建3D模型和場(chǎng)景的技術(shù),您可以在屏幕上動(dòng)態(tài)更新。在visionOS中,同時(shí)使用RealityKit和SwiftUI將應(yīng)用程序的2D和3D內(nèi)容無(wú)縫耦合。加載現(xiàn)有的USDZ資產(chǎn)或在Reality Composer Pro中創(chuàng)建場(chǎng)景,其中包含動(dòng)畫、物理、燈光、聲音和內(nèi)容的自定義行為。要在應(yīng)用程序中使用Reality Composer Pro項(xiàng)目,請(qǐng)將Swift軟件包添加到Xcode項(xiàng)目中,并在Swift文件中導(dǎo)入其模塊。有關(guān)更多信息,請(qǐng)參閱在Xcode項(xiàng)目中管理文件和文件夾。

當(dāng)您準(zhǔn)備在界面中顯示3D內(nèi)容時(shí),請(qǐng)使用RealityView。此SwiftUI視圖作為RealityKit內(nèi)容的容器,并允許您使用熟悉的SwiftUI技術(shù)更新該內(nèi)容。
以下示例顯示了使用RealityView顯示3D球體的視圖。視圖閉包中的代碼為球體創(chuàng)建一個(gè)RealityKit實(shí)體,將紋理應(yīng)用于球體的表面,并將球體添加到視圖的內(nèi)容中。
struct SphereView: View {
var body: some View {
RealityView { content in
let model = ModelEntity(
mesh: .generateSphere(radius: 0.1),
materials: [SimpleMaterial(color: .white, isMetallic: true)])
content.add(model)
}
}
}
當(dāng)SwiftUI顯示您的RealityView時(shí),它會(huì)執(zhí)行一次您的代碼以創(chuàng)建實(shí)體和其他內(nèi)容。由于創(chuàng)建實(shí)體相對(duì)昂貴,視圖只運(yùn)行一次您的創(chuàng)建代碼。當(dāng)您想更新實(shí)體的狀態(tài)時(shí),請(qǐng)更改視圖的狀態(tài),并使用更新閉包將這些更改應(yīng)用于您的內(nèi)容。當(dāng)縮放屬性中的值發(fā)生變化時(shí),以下示例使用更新閉包來(lái)更改球體的大?。?/p>
struct SphereView: View {
var scale = false
var body: some View {
RealityView { content in
let model = ModelEntity(
mesh: .generateSphere(radius: 0.1),
materials: [SimpleMaterial(color: .white, isMetallic: true)])
content.add(model)
} update: { content in
if let model = content.entities.first {
model.transform.scale = scale ? [1.2, 1.2, 1.2] : [1.0, 1.0, 1.0]
}
}
}
}
有關(guān)如何使用RealityKit創(chuàng)建內(nèi)容的信息,請(qǐng)參閱RealityKit。
響應(yīng)與RealityKit內(nèi)容的交互
要處理與RealityKit場(chǎng)景實(shí)體的交互:
?將手勢(shì)識(shí)別器附加到您的RealityView,并將targetedToAnyEntity()修飾符添加到其中。
?將InputTargetComponent附加到實(shí)體或其父實(shí)體之一。
?向支持交互的RealityKit實(shí)體添加碰撞形狀。
targetedToAnyEntity()修飾符在手勢(shì)識(shí)別器和您的RealityKit內(nèi)容之間架起了一座橋梁。例如,要識(shí)別某人何時(shí)拖動(dòng)實(shí)體,請(qǐng)指定DragGesture并向其添加修飾符。當(dāng)指定的手勢(shì)發(fā)生在實(shí)體上時(shí),SwiftUI執(zhí)行提供的閉包。
以下示例將點(diǎn)擊手勢(shì)識(shí)別器添加到上一個(gè)示例的球形視圖中。該代碼還將InputTargetComponent和CollisionComponent組件添加到形狀中,以允許交互發(fā)生。如果您省略這些組件,視圖將不會(huì)檢測(cè)到與您的實(shí)體的交互。
struct SphereView: View {
@State private var scale = false
var body: some View {
RealityView { content in
let model = ModelEntity(
mesh: .generateSphere(radius: 0.1),
materials: [SimpleMaterial(color: .white, isMetallic: true)])
// Enable interactions on the entity.
model.components.set(InputTargetComponent())
model.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.1)]))
content.add(model)
} update: { content in
if let model = content.entities.first {
model.transform.scale = scale ? [1.2, 1.2, 1.2] : [1.0, 1.0, 1.0]
}
}
.gesture(TapGesture().targetedToAnyEntity().onEnded { _ in
scale.toggle()
})
}
}
在Volume中顯示3D內(nèi)容
Volume是一種以三維方式增長(zhǎng)的窗口,以匹配其包含的內(nèi)容的大小。Windows和Volume都可以容納2D和3D內(nèi)容,并且在許多方面是相似的。然而,Windows剪輯3D內(nèi)容從窗口表面延伸得太遠(yuǎn),因此Volume是主要是3D內(nèi)容的更好選擇。
要?jiǎng)?chuàng)建Volume,請(qǐng)將WindowGroup場(chǎng)景添加到您的應(yīng)用程序中,并將其樣式設(shè)置為體積。此樣式告訴SwiftUI為3D內(nèi)容創(chuàng)建一個(gè)窗口。在Volume中包含您想要的任何2D或3D視圖。您還可以添加RealityView來(lái)使用RealityKit構(gòu)建內(nèi)容。以下示例使用存儲(chǔ)在應(yīng)用程序捆綁包中的一些氣球的靜態(tài)3D模型創(chuàng)建一個(gè)Volume:
struct MyApp: App {
var body: some Scene {
WindowGroup {
Model3D("balloons")
}.windowStyle(style: .volumetric)
}
}
Windows和Volume是顯示有界2D和3D內(nèi)容的便捷方式,但您的應(yīng)用程序不會(huì)控制該內(nèi)容在人周圍的位置。系統(tǒng)在顯示時(shí)設(shè)置每個(gè)窗口和音量的初始位置。該系統(tǒng)還添加了一個(gè)窗口欄,允許某人重新定位窗口或調(diào)整其大小。

有關(guān)何時(shí)使用Volume的更多信息,請(qǐng)參閱人機(jī)界面指南>Windows。
在一個(gè)人的環(huán)境中顯示3D內(nèi)容
當(dāng)您需要對(duì)應(yīng)用程序內(nèi)容的位置進(jìn)行更多控制時(shí),請(qǐng)將該內(nèi)容添加到ImmersiveSpace中。沉浸式空間為您的內(nèi)容提供了一個(gè)無(wú)界區(qū)域,您可以控制空間內(nèi)內(nèi)容的大小和位置。獲得用戶許可后,您還可以使用帶有沉浸式空間的ARKit將內(nèi)容集成到周圍環(huán)境中。例如,您可以使用ARKit場(chǎng)景重建來(lái)獲取家具和附近對(duì)象的網(wǎng)格,并讓您的內(nèi)容與該網(wǎng)格交互。
ImmersiveSpace是您與應(yīng)用程序的其他場(chǎng)景一起創(chuàng)建的場(chǎng)景類型。以下示例顯示了一個(gè)包含沉浸式空間和窗口的應(yīng)用程序:
@main
struct MyImmersiveApp: App {
var body: some Scene {
WindowGroup() {
ContentView()
}
ImmersiveSpace(id: "solarSystem") {
SolarSystemView()
}
}
}
如果您沒有在ImmersiveSpace聲明中添加樣式修飾符,系統(tǒng)將使用mixed樣式創(chuàng)建該空間。此樣式顯示您的內(nèi)容以及顯示該人周圍環(huán)境的傳遞內(nèi)容。其他樣式允許您在不同程度上隱藏直通。使用 immersionStyle(selection:in:)修飾符來(lái)指定您的空間支持哪些樣式。如果您指定了多個(gè)樣式,您可以使用修飾符的選擇參數(shù)在樣式之間切換。
警告:
注意您在使用混合風(fēng)格的沉浸式場(chǎng)景中包含多少內(nèi)容。填充屏幕很大一部分的內(nèi)容,即使該內(nèi)容部分是透明的,也可以防止人們看到周圍環(huán)境的潛在危險(xiǎn)。如果您想讓人沉浸在您的內(nèi)容中,請(qǐng)以完整的風(fēng)格配置您的空間。有關(guān)更多信息,請(qǐng)參閱在應(yīng)用程序中創(chuàng)建完全身臨其境的體驗(yàn)。
請(qǐng)記住設(shè)置您放置在沉浸式空間中的物品的位置。使用修飾符定位SwiftUI視圖,并使用其轉(zhuǎn)換組件定位RealityKit實(shí)體。SwiftUI最初將空間的起源放在人的腳下,但可以根據(jù)其他事件改變這個(gè)起源。例如,系統(tǒng)可能會(huì)移動(dòng)原點(diǎn),以適應(yīng)SharePlay活動(dòng),該活動(dòng)以空間角色顯示您的內(nèi)容。如果您需要將SwiftUI視圖和RealityKit實(shí)體相對(duì)定位,請(qǐng)使用RealityView內(nèi)容參數(shù)中的方法執(zhí)行任何所需的坐標(biāo)轉(zhuǎn)換。
要顯示您的ImmersiveSpace場(chǎng)景,請(qǐng)使用您從SwiftUI環(huán)境獲得的openImmersiveSpace操作打開它。此操作異步運(yùn)行,并使用提供的信息來(lái)查找和初始化您的場(chǎng)景。以下示例顯示了一個(gè)用太陽(yáng)系標(biāo)識(shí)符打開空間的按鈕:
Button("Show Solar System") {
Task {
let result = await openImmersiveSpace(id: "solarSystem")
if case .error = result {
print("An error occurred")
}
}
}
當(dāng)應(yīng)用程序顯示ImmersiveSpace時(shí),系統(tǒng)會(huì)隱藏其他應(yīng)用程序的內(nèi)容,以防止視覺沖突。當(dāng)您的空間可見時(shí),其他應(yīng)用程序保持隱藏狀態(tài),但在您關(guān)閉它時(shí)返回。如果您的應(yīng)用程序定義了多個(gè)空間,您必須在顯示其他空間之前關(guān)閉當(dāng)前可見的空間。如果您不關(guān)閉可見空間,當(dāng)您嘗試打開另一個(gè)空間時(shí),系統(tǒng)會(huì)發(fā)出運(yùn)行時(shí)警告。