How to update immersiveSpace from another window

Hi I have 2 views and an Immersive space. 1st and 2nd views are display in a TabView I open my ImmersiveSpace from a button in the 1st view of the tab. Then When I go to 2nd TabView I want to show an attachment in my Immersive space. This attachment should be visible in Immersive space only as long as the user os on the 2nd view. This is what I have done so far

struct Second: View {
    @StateObject var sharedImageData = SharedImageData()

    var body: some View {
        VStack {
             // other code
         } .onAppear() {
            Task {
                sharedImageData.shouldCameraButtonShouw = true
            }
        }
        .onDisappear() {
            Task {
                sharedImageData.shouldCameraButtonShouw = false
            }
        }
    }
}

This is my Immersive space

struct ImmersiveView: View {
     @EnvironmentObject var sharedImageData: SharedImageData

     var body: some View {
         RealityView { content, attachments in
         // some code
        } update: { content, attachments in
               guard let controlCenterAttachmentEntity =            

attachments.entity(for: Attachments.controlCenter) else { return }
                    
                controlCenterentity.addChild(controlCenterAttachmentEntity)
                content.add(controlCenterentity)
          } attachments: {

                   if sharedImageData.shouldCameraButtonShouw {
                Attachment(id: Attachments.controlCenter) {
                    ControlCenter()
                }
            }
            }
     }
}

And this is my Observable class

class SharedImageData: ObservableObject {
    @Published var takenImage: UIImage? = nil
    @Published var shouldCameraButtonShouw: Bool = false
}

My problem is, when I am on Second view my attachment never appears. Attachment appears without this if condition. But How can I achieve my goal?

Answered by Vision Pro Engineer in 801726022

Hello @rands

The value of a property with the @EnvironmentObject annotation must be supplied by a parent or ancestor view. Additionally, in Second, you have declared SharedImageData with the @StateObject annotation, which creates a brand new instance. This means that when Second sets sharedImageData.shouldCameraButtonShouw to true, it is updating the value of its own SharedImageData instance, rather than the one inside ImmersiveView.

To fix this, swap the @StateObject annotation in Second with @EnvironmentObject:

struct Second: View {
    @EnvironmentObject var sharedImageData: SharedImageData

    var body: some View {
        // ...
    }
}

Then when you declare Second, you will need to pass in sharedImageData via the .environmentObject() API. You are likely doing this already for ImmersiveView.

One more thing: I also noticed that in your update closure, you are adding controlCenterAttachmentEntity every time this block is called (i.e., when the state changes). This is probably not necessary and may cause other issues in your app. Instead of putting your attachment behind an if check, create your attachment regardless and move the if check to update, where you can enable/disable the controlCenterAttachmentEntity to toggle the visibility of your attachment.

Check out the Diorama sample project for an example of how to conditionally show and hide attachments.

Good luck!

Accepted Answer

Hello @rands

The value of a property with the @EnvironmentObject annotation must be supplied by a parent or ancestor view. Additionally, in Second, you have declared SharedImageData with the @StateObject annotation, which creates a brand new instance. This means that when Second sets sharedImageData.shouldCameraButtonShouw to true, it is updating the value of its own SharedImageData instance, rather than the one inside ImmersiveView.

To fix this, swap the @StateObject annotation in Second with @EnvironmentObject:

struct Second: View {
    @EnvironmentObject var sharedImageData: SharedImageData

    var body: some View {
        // ...
    }
}

Then when you declare Second, you will need to pass in sharedImageData via the .environmentObject() API. You are likely doing this already for ImmersiveView.

One more thing: I also noticed that in your update closure, you are adding controlCenterAttachmentEntity every time this block is called (i.e., when the state changes). This is probably not necessary and may cause other issues in your app. Instead of putting your attachment behind an if check, create your attachment regardless and move the if check to update, where you can enable/disable the controlCenterAttachmentEntity to toggle the visibility of your attachment.

Check out the Diorama sample project for an example of how to conditionally show and hide attachments.

Good luck!

How to update immersiveSpace from another window
 
 
Q