Consider a UIViewRepresentable
configured like this, for use with RealityKit
:
struct ARViewContainer: UIViewRepresentable {
let arView = ARView(frame: .zero, cameraMode: ARView.CameraMode.nonAR, automaticallyConfigureSession: false)
func makeUIView(context: Context) -> ARView {
//My configurations here
}
func updateUIView(_ view: ARView, context: Context) { }
}
If we add a gesture to the SwiftUI view overlaying ARViewContainer
and change a @State
variable that then alters the view's scaleEffect
modifier, the RealityKit scene freezes:
struct ContentView: View {
@State private var scaleDown: Bool = false
var body: some View {
ZStack {
ARViewContainer()
.ignoresSafeArea()
Image("button")
.resizable()
.scaleEffect(self.scaleDown ? 0.9 : 1.0)
.frame(width: 130.0, height: 130.0)
.position(x: 100.0, y: 100.0)
.gesture(DragGesture(minimumDistance: 0).onChanged { touch in
//This triggers a freeze:
self.scaleDown = true
}.onEnded { touch in
//This triggers a freeze:
self.scaleDown = false
})
}
}
}
I've also found that changing any @Published
value from inside the following .subscribe
closure results in the same misbehavior -- even if that value is not being applied to anything.
sceneObserver = self.arView.scene.subscribe(to: SceneEvents.Update.self) { context in
//This would also cause the RealityKit scene to visually freeze, even though somePublishedInteger does nothing but increment:
somePublishedInteger += 1
}
Tested on real devices: iPhone 14, iPad (9th generation)
Question: What's going wrong, here, and how do I fix it?
Creating an ARView object inside a ContentView does the trick: ARView isn't frozen now.
import SwiftUI
import RealityKit
struct ARViewContainer : UIViewRepresentable {
let arView: ARView
func makeUIView(context: Context) -> ARView {
let box = ModelEntity(mesh: .generateBox(size: 0.25))
let anchor = AnchorEntity()
anchor.addChild(box)
arView.scene.addAnchor(anchor)
box.move(
to: Transform(roll: .pi),
relativeTo: nil,
duration: 15,
timingFunction: .linear
)
return arView
}
func updateUIView(_ view: ARView, context: Context) { }
}
struct ContentView : View {
let arView = ARView(
frame: .zero,
cameraMode: .nonAR,
automaticallyConfigureSession: true
)
@State var scaleDown: Bool = false
var body: some View {
ZStack {
ARViewContainer(arView: arView)
.ignoresSafeArea()
Image(systemName: "swift")
.resizable()
.foregroundStyle(.white)
.scaleEffect(scaleDown ? 0.7 : 1.0)
.frame(width: 50, height: 50)
.position(x: 200.0, y: 100.0)
.gesture(
DragGesture()
.onChanged { _ in
scaleDown = true
Task {
try await Task.sleep(nanoseconds: UInt64(1e8))
scaleDown = false
}
}
)
}
}
}