Canvatorium Visio Lab 5021
Mixing 2D Views with RealityViews in SwiftUI.
Not everything needs to be an attachment. Sometimes it can be better to mix 2D and 3D content in SwiftUI instead of inside a RealityView.
I used this approach in Project Graveyard to show 2D cards above the 3D content in a volume. It has some limitations.
- No access to SwiftUI views that use presentation: modals, sheets, etc.
- When selecting a text field, the system can sometimes position the keyboard or inside the 3D content
struct Lab5021: View {
@State var fruit: String?
var body: some View {
// 1. Use a ZStack to mix SwiftUI Views with RealtyView
ZStack(alignment: Alignment(horizontal: .center, vertical: .center)) {
// 2. Create a view that we can use to show details when tapping a mesh
if(fruit != nil) {
VStack {
Text("Not a window, only a view")
.font(.title)
Text("selected: \(fruit ?? "")")
.font(.headline)
}
.padding(48)
.glassBackgroundEffect(in: .rect(cornerRadius: 24))
.frame(width: 800, height: 500)
.offset(y: -60)
}
// 3. Add our RealityView content
// NOTE: We need to use InputTargetComponent and CollisionComponent on any Entity can should receive input (tap, drag, etc.)
RealityView { content in
var collision = CollisionComponent(shapes: [.generateSphere(radius: 0.05)])
collision.filter = CollisionFilter(group: [], mask: [])
let model = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .green, isMetallic: false)])
model.name = "Apple"
model.position = [-0.1,-0.2,0]
model.components.set(collision)
model.components.set(InputTargetComponent())
content.add(model)
let model2 = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .orange, isMetallic: false)])
model2.name = "Orange"
model2.position = [0.1,-0.2,0]
model2.components.set(collision)
model2.components.set(InputTargetComponent())
content.add(model2)
}
// 5. Add the tap
.gesture(tap)
}
}
// Create a tap gesture. Set fruit to the tapped entity name
var tap: some Gesture {
SpatialTapGesture()
.targetedToAnyEntity()
.onEnded { value in
fruit = fruit == value.entity.name ? nil : value.entity.name
}
}
}