Spatial SwiftUI: Presentation Breakthrough Effect
We can use this new modifier to override the system default breakthrough effect for presentations.
We learned how to use Breakthrough Effect to make sure our SwiftUI content stays visible. Presentations will use breakthrough effects by default in most cases. If we want to remove or customize the effects, we can use presentationBreakthroughEffect.
fileprivate struct EarthCard: View {
var body : some View {
VStack {
Text("Earth")
.font(.title)
Text("The only known planet known to serve ice cream.")
.font(.caption)
}
.padding()
.presentationBreakthroughEffect(.prominent)
}
}The list of effects are the same as breakthroughEffect.
- automatic: let visionOS decide in an effect should be applied (default even when not using this modifier)
- none: never apply an effect
- subtle: apply a subtle effect
- prominent: apply a prominent effect
This effect works much the same way as breakthroughEffect. We can use that to add effects to view. We can use presentationBreakthroughEffect to remove or customize effects from presented views.
Note: make sure you try this on a real device. There has been a known issue in the visionOS Beta release notes that this feature does not work in the Simulator.
Video Demo
Example Code
fileprivate struct EarthCard: View {
@Binding var effect: BreakthroughEffect
var body : some View {
VStack {
Text("Earth")
.font(.title)
Text("The only known planet known to serve ice cream.")
.font(.caption)
}
.padding()
.presentationBreakthroughEffect(effect)
}
}
struct Example099: View {
@State private var effect: BreakthroughEffect = .none
@State private var showingPopover: Bool = false
var body: some View {
VStack {
Spacer()
RealityView { content in
guard let scene = try? await Entity(named: "PresentingEarth", in: realityKitContentBundle) else { return }
content.add(scene)
guard let subject = scene.findEntity(named: "Earth") else { return }
guard let moon = scene.findEntity(named: "Moon") else { return }
moon.components.set(ManipulationComponent())
// A secondary entity that we can use as the transform point for the presented popover
guard let presentationPoint = scene.findEntity(named: "PresentationPoint") else { return }
// We'll use a TapGesture and the new GestureComponent to toggle the popover
let tapGesture = TapGesture()
.onEnded({
Entity.animate(.bouncy, body: {
showingPopover.toggle()
})
})
let gestureComponent = GestureComponent(tapGesture)
subject.components.set(gestureComponent)
// Create the presentation component using $showingPopover to toggle presentation
let presentation = PresentationComponent(
isPresented: $showingPopover,
configuration: .popover(arrowEdge: .bottom),
content: EarthCard(effect: $effect)
)
presentationPoint.components.set(presentation)
}
.realityViewLayoutBehavior(.fixedSize)
}
// Controls to modify the example
.ornament(attachmentAnchor: .scene(.bottomTrailing), contentAlignment: .bottomTrailing, ornament: {
VStack(alignment: .center, spacing: 8) {
Button(action: {
withAnimation {
effect = .none
}
}, label: {
Text("None")
})
Button(action: {
withAnimation {
effect = .subtle
}
}, label: {
Text("Subtle")
})
Button(action: {
withAnimation {
effect = .prominent
}
}, label: {
Text("Prominent")
})
}
.padding()
.controlSize(.extraLarge)
.glassBackgroundEffect()
})
}
}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