Reputation: 2827
The concrete usecase is that I want to wrap a ARSCNView
in UIViewRepresentable
.
The thing is, I want to expose the ARSCNView
's following properties:
.delegate
.session.delegate
.session.delegateQueue
and consider that some of the ARSCNViewDelegate
method signatures have return values:
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?
and I'm using this view in a pattern that has a
@StateObject var viewModel: ViewModel // where ViewModel runs on @MainActor
Now, I do have an approach that sort of works, but it doesn't handle the concurrency well, since any message sent to the viewModel would occur on the MainActor, and then the .session.delegateQueue
would essentially be useless, no?
This is what I have so far, but to me, it "smells funny":
struct ARSCNSwiftUIView: UIViewRepresentable {
let sessionConfiguration: ARConfiguration
@Binding var isRunning: Bool
let onRendererNeedsNodeForAnchor: (SCNSceneRenderer, ARAnchor) -> SCNNode?
let onRendererDidUpdateNodeForAnchor: (SCNSceneRenderer, SCNNode, ARAnchor) -> Void
let onARSessionDidUpdateFrame: (ARSession, ARFrame) -> Void
func makeUIView(context: Context) -> ARSCNView {
let view = ARSCNView()
guard ARWorldTrackingConfiguration.isSupported else { return view }
view.delegate = context.coordinator
view.session.delegate = context.coordinator
// TODO: view.session.delegateQueue = coordinator.workerQueue then a view model has a nonisolated method to handle these?
#if DEBUG
view.showsStatistics = true
#endif
return view
}
func updateUIView(_ view: ARSCNView, context: Context) {
if self.isRunning {
view.session.run(self.sessionConfiguration, options: [.resetTracking, .removeExistingAnchors])
} else {
view.session.pause()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, ARSCNViewDelegate, ARSessionDelegate {
var parent: ARSCNSwiftUIView
init(_ parent: ARSCNSwiftUIView) {
self.parent = parent
}
func session(_ session: ARSession, didUpdate frame: ARFrame) {
self.parent.onARSessionDidUpdateFrame(session, frame)
}
public func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
return self.parent.onRendererNeedsNodeForAnchor(renderer, anchor)
}
public func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
self.parent.onRendererDidUpdateNodeForAnchor(renderer, node, anchor)
}
}
}
Upvotes: 0
Views: 175
Reputation: 30746
you cant use self
because it's a struct value not a object reference so change to:
func makeCoordinator() -> Coordinator {
Coordinator()
}
Also you need to use the new version of the closures, e.g.
func updateUIView(_ view: ARSCNView, context: Context) {
context.coordinator.onARSessionDidUpdateFrame = onARSessionDidUpdateFrame
// etc.
class Coordinator: NSObject, ARSCNViewDelegate, ARSessionDelegate {
var onARSessionDidUpdateFrame: ((ARSession, ARFrame) -> Void)?
func session(_ session: ARSession, didUpdate frame: ARFrame) {
onARSessionDidUpdateFrame(session, frame)
}
Upvotes: 0