I'm trying to create a function that pauses AR session and also clears my dictionary variable entityDictionaries and removes all anchors. However, I'm kinda stuck and don't know how to start.
Here is a code for my main view:
struct InitiateARView: View {
@StateObject var vm = ARPreparationViewModel()
var body: some View {
VStack {
if vm.isLoading {
Button("Start", action: {
vm.prepareAR()
})
} else {
ZStack {
ARExperience()
.edgesIgnoringSafeArea(.all)
VStack {
HStack {
Spacer()
Button("Stop AR", action: {
// Call to clear AR session function here
})
}
Spacer()
}
}
}
}
}
}
And here is simplified version of my ARExperience struct
struct ARExperience: UIViewRepresentable {
var arView = ARView(frame: .zero)
var entityDictionaries = [String: (anchor: AnchorEntity, model: ModelEntity)]()
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject, ARSessionDelegate {
var parent: ARExperience
init(parent: ARExperience) {
self.parent = parent
}
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
guard let validAnchor = anchors[0] as? ARImageAnchor else { return }
let anchor = AnchorEntity(anchor: validAnchor)
let videoPlane = createdVideoPlayerNodeFor(validAnchor.referenceImage)
anchor.addChild(videoPlane)
parent.arView.scene.addAnchor(anchor)
if let targetName = validAnchor.referenceImage.name {
parent.entityDictionaries[targetName] = (anchor, videoPlane)
}
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
}
}
func makeUIView(context: Context) -> ARView {
//...
}
func updateUIView(_ uiView: ARView, context: Context) {
}
}
Any help that can get me 'unstuck' with this problem will be perfect
One way to do it is to separate UIKit from SwiftUI and have a Common source of truth where the 2 frameworks can communicate with each other.
struct InitiateARView: View {
@StateObject var vm = ARPreparationViewModel()
var body: some View {
VStack {
if vm.isLoading {
Button("Start", action: {
vm.prepareAR()
})
} else {
ZStack {
ARExperience_UI(vm: vm) //Your experiece would change to include the shared VM
.edgesIgnoringSafeArea(.all)
VStack {
HStack {
Spacer()
Button("Stop AR", action: {
//Make SwiftUI changes
vm.clearAR() // Then you can access UIKit via the VM
})
}
Spacer()
}
}
}
}
}
}
The shared source of truth would look something like.
//Works with both SwiftUI and UIKit
class ARPreparationViewModel: ObservableObject{
//reference to UIKit, will be nil until set by ViewController init.
@Published var vc: ARExperienceVC?
@Published var isLoading: Bool = false
deinit {
vc = nil
}
func prepareAR() {
guard let vc = vc else {
print("Missing View Controller")
return
}
vc.prepareAR()
}
func clearAR() {
guard let vc = vc else {
print("Missing View Controller")
return
}
//Make any source of truth changes
vc.clearAR()
}
}
Then the UIViewControllerRepresentable code would be simplified to just create the new UIViewController, this just serves as a bridge.
// SwiftUI
struct ARExperience_UI: UIViewControllerRepresentable { //Change to a UIViewControllerRepresentable
let vm: ARPreparationViewModel
func makeUIViewController(context: Context) -> some UIViewController {
ARExperienceVC(vm: vm) // Create the new ViewController with all the UIKit Code
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
The UIKit code does the heavy lifting.
// Keep UIKit code here
class ARExperienceVC: UIViewController {
let vm: ARPreparationViewModel
lazy var arView: ARView = {
let arView = ARView(frame: .zero)
arView.session.delegate = self
return arView
}()
init(vm: ARPreparationViewModel) {
self.vm = vm
super.init(nibName: nil, bundle: nil)
self.vm.vc = self //Without this the source of truth won't have access to the ViewController
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.vm.vc = nil
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(arView)
arView.pinToSuperview(self.view) // This lest SwiftUI control the size because the view will be attached to the bordering anchors.
}
func prepareAR() {
print("\(type(of: self)) :: \(#function)")
}
func clearAR() {
print("\(type(of: self)) :: \(#function)")
//Make UIKit changes.
}
}
extension ARExperienceVC: ARSessionDelegate {
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
}
}
extension UIView{
func pinToSuperview(_ superView: UIView){
self.translatesAutoresizingMaskIntoConstraints = false
self.topAnchor.constraint(equalTo: superView.topAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: superView.bottomAnchor).isActive = true
self.leadingAnchor.constraint(equalTo: superView.leadingAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: superView.trailingAnchor).isActive = true
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With