// // ImmersiveView.swift // TestVideoLeakOneImmersiveView // import AVKit import OSLog import SwiftUI import RealityKit import RealityKitContent struct ImmersiveView: View { let logger = Logger(subsystem: SUBSYSTEM, category: "ImmersiveView") @Environment(\.scenePhase) var scenePhase @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace var videoViewModel: Immersive180VideoViewModel? = { guard let url = Bundle.main.url(forResource: "a_large_650mb_video_file", withExtension: "mov") else { assertionFailure("missing video") return nil } return Immersive180VideoViewModel(url: url) }() private let rootEntity = Entity() private let immersiveS3180VideoEntity = Entity() @State private var isShowImmsersiveVideo = false var body: some View { RealityView { content, attachments in logger.info("\(#function) \(#line) `content, attachments in`") updateScene(content: content, attachments: attachments) } update: { content, attachments in logger.info("\(#function) \(#line) `update: { content, attachments in`") updateScene(content: content, attachments: attachments) } placeholder: { let _ = logger.info("\(#function) \(#line) `placeholder`") ProgressView() } attachments: { let _ = logger.info("\(#function) \(#line) `attachments`") Attachment(id: "closeImmersiveSpaceButton") { Button { // show hide immsersive video isShowImmsersiveVideo.toggle() } label: { Text(isShowImmsersiveVideo ? "Hide Immersive Video" : "Show Immersive Video") } } } .onChange(of: scenePhase, { oldValue, newValue in logger.info("\(#function) \(#line) `onChange(of: scenePhase` scenePhase oldValue \(String(describing: oldValue)) scenePhase newValue \(String(describing: newValue))") switch newValue { case .active: logger.info("\(#function) \(#line) scenePhase newValue \(String(describing: newValue))") case .background: logger.info("\(#function) \(#line) scenePhase newValue \(String(describing: newValue))") unloadScenes() case .inactive: logger.info("\(#function) \(#line) scenePhase newValue \(String(describing: newValue))") @unknown default: logger.warning("\(#function) \(#line) scenePhase newValue \(String(describing: newValue))") } }) } func updateScene(content: RealityViewContent, attachments: RealityViewAttachments) { logger.info("\(#function) \(#line)") guard scenePhase == .active else { logger.info("\(#function) \(#line) escaping since app backgrounded") return } if rootEntity.children.isEmpty { Task{ await loadScenes(content: content, attachments: attachments) } } else { controlScene(content: content, attachments: attachments) } } func loadScenes(content: RealityViewContent, attachments: RealityViewAttachments) async { logger.info("\(#function) \(#line)") guard let closeImmersiveSpaceButton = attachments.entity(for: "closeImmersiveSpaceButton") else { logger.error("\(#function) \(#line) missing attachment") assertionFailure("missing attachment") return } closeImmersiveSpaceButton.position = [0.0, 0.1, -2.5] closeImmersiveSpaceButton.scale = scaleForAttachements rootEntity.addChild(closeImmersiveSpaceButton) content.add(rootEntity) loadS3180Video(content: content) } func unloadScenes() { logger.info("\(#function) \(#line)") if let videoViewModel { videoViewModel.reset() } immersiveS3180VideoEntity.children.removeAll() rootEntity.children.removeAll() Task { await dismissImmersiveSpace() } } func loadS3180Video(content: RealityViewContent) { logger.info("\(#function) \(#line)") guard let videoViewModel else { logger.error("\(#function) \(#line) missing model") assertionFailure("missing view model") return } let entity = videoViewModel.setupContentEntity() immersiveS3180VideoEntity.addChild(entity) immersiveS3180VideoEntity.isEnabled = false rootEntity.addChild(immersiveS3180VideoEntity) } func hideVideo() { logger.info("\(#function) \(#line)") guard let videoViewModel else { logger.error("\(#function) \(#line) missing model") assertionFailure("missing model") return } immersiveS3180VideoEntity.isEnabled = false videoViewModel.stop() } func showVideo() { logger.info("\(#function) \(#line)") guard let videoViewModel else { logger.error("\(#function) \(#line) missing model") assertionFailure("missing model") return } immersiveS3180VideoEntity.isEnabled = true videoViewModel.play() } func controlScene(content: RealityViewContent, attachments: RealityViewAttachments) { logger.info("\(#function) \(#line)") if isShowImmsersiveVideo { showVideo() } else { hideVideo() } } } #Preview(immersionStyle: .mixed) { ImmersiveView() .environment(AppModel()) }