Explore Viewing Modes with Image Presentation Component
The available viewing modes depend on the type of image the component is presenting.
We learned how to load photos with Image Presentation Component. Let’s take a quick look (haha) at the supported viewing modes.
- mono
- spatialStereo
- spatialStereoImmersive
- spatial3D
- spatial3DImmersive
We can check to see the current active mode by reading from viewingMode
component.viewingMode // monoWe can check availableViewingModes for a list of supported modes, which is based on the type of image.
if component.availableViewingModes.contains(.spatial3D) {
...
}Then we can set desiredViewingMode to request a mode change.
if component.availableViewingModes.contains(.spatial3D) {
component.desiredViewingMode = .spatial3D
}Here is a quick breakdown of the modes available to each image type.
| Standard Photo | Spatial Photo | Spatial Scene |
| mono | mono | mono |
| spatialStereo | ||
| spatialStereoImmersive | ||
| spatial3D | ||
| spatial3DImmersive |
Note: We haven’t tried generating a Spatial Scene from a Spatial Photo yet. Do you think it would work? If so, would this new Super Spatial Scene support all five modes?
Video Demo
Example Code
struct Example128: View {
@State var photoEntity = Entity()
@State private var currentMode = ImagePresentationComponent.ViewingMode.mono
@State private var availableModes: Set<ImagePresentationComponent.ViewingMode> = []
var body: some View {
RealityView { content in
photoEntity.setPosition([0, 1.6, -2.0], relativeTo: nil)
photoEntity.scale = .init(repeating: 0.6)
content.add(photoEntity)
// Attach SwiftUI controls into the scene
let controlMenu = Entity()
let controlAttachment = ViewAttachmentComponent(
rootView: ControlsPanel(
currentMode: $currentMode,
availableModes: $availableModes,
loadPhoto: { Task { await loadPhoto(entity: photoEntity) } },
loadSpatialPhoto: { Task { await loadSpatialPhoto(entity: photoEntity) } },
loadPhotoToConvert: { Task { await loadPhotoToConvert(entity: photoEntity) } }
)
)
controlMenu.components.set(controlAttachment)
controlMenu.setPosition([0, 1.2, -1.8], relativeTo: nil)
content.add(controlMenu)
}
// Listen for changes to mode and update the component
.onChange(of: currentMode, { _, newValue in
photoEntity.components[ImagePresentationComponent.self]?.desiredViewingMode = newValue
})
}
/// Load a regular (non-spatial) photo
func loadPhoto(entity: Entity) async {
guard let url = Bundle.main.url(forResource: "bell-01", withExtension: "jpeg") else { return }
do {
let component = try await ImagePresentationComponent(contentsOf: url)
availableModes = component.availableViewingModes
entity.components.set(component)
} catch {
print("Failed to load image: \(error)")
}
}
/// Load a spatial photo (captured on iPhone 17 Pro)
func loadSpatialPhoto(entity: Entity) async {
guard let url = Bundle.main.url(forResource: "bell-01-s", withExtension: "HEIC") else { return }
do {
var component = try await ImagePresentationComponent(contentsOf: url)
availableModes = component.availableViewingModes
if availableModes.contains(.spatialStereo) {
component.desiredViewingMode = .spatialStereo
}
entity.components.set(component)
} catch {
print("Failed to load image: \(error)")
}
}
/// Load a regular (non-spatial) photo, then convert it to a Spatial Scene
func loadPhotoToConvert(entity: Entity) async {
guard let url = Bundle.main.url(forResource: "bell-01", withExtension: "jpeg") else { return }
do {
let converted = try await ImagePresentationComponent.Spatial3DImage(contentsOf: url)
try await converted.generate()
var component = ImagePresentationComponent(spatial3DImage: converted)
availableModes = component.availableViewingModes
if availableModes.contains(.spatial3D) {
component.desiredViewingMode = .spatial3D
}
entity.components.set(component)
} catch {
print("Failed to load image: \(error)")
}
}
}
// Moving the control panel to a view
struct ControlsPanel: View {
@Binding var currentMode: ImagePresentationComponent.ViewingMode
@Binding var availableModes: Set<ImagePresentationComponent.ViewingMode>
let loadPhoto: () -> Void
let loadSpatialPhoto: () -> Void
let loadPhotoToConvert: () -> Void
var body: some View {
VStack(spacing: 12) {
HStack(spacing: 12) {
Button(action: {
loadPhoto()
}, label: {
Text("Photo")
})
Button(action: {
loadSpatialPhoto()
}, label: {
Text("Spatial Photo")
})
Button(action: {
loadPhotoToConvert()
}, label: {
Text("Spatial Scene")
})
}
.controlSize(.extraLarge)
HStack {
Button(action: {
currentMode = .mono
}, label: {
Text("mono")
})
.disabled(!availableModes.contains(.mono))
Button(action: {
currentMode = .spatialStereo
}, label: {
Text("spatialStereo")
})
.disabled(!availableModes.contains(.spatialStereo))
Button(action: {
currentMode = .spatialStereoImmersive
}, label: {
Text("spatialStereoImmersive")
})
.disabled(!availableModes.contains(.spatialStereoImmersive))
Button(action: {
currentMode = .spatial3D
}, label: {
Text("spatial3D")
})
.disabled(!availableModes.contains(.spatial3D))
Button(action: {
currentMode = .spatial3DImmersive
}, label: {
Text("spatial3DImmersive")
})
.disabled(!availableModes.contains(.spatial3DImmersive))
}
.controlSize(.small)
.padding()
}
.padding()
.background(.black)
.clipShape(.capsule)
}
}Support our work so we can continue to bring you new examples and articles.
Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Follow Step Into Vision