在沉浸式空间中显示加载动画,直到 Av Player 在 Vision OS 中播放视频

问题描述 投票:0回答:1

在沉浸式视图中显示加载动画,直到 av 播放器从 url 加载视频,还有什么方法可以将动态 url 传递到此视图,因为它位于应用程序结构内部,当应用程序加载到内存中时,该结构会被初始化

struct ImmersiveMeditationPlayerView: View {
    @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace
    @Environment(\.openWindow) private var openWindow
    let player = AVPlayer()
    
    var body: some View {
        VStack {
            RealityView { content, attachments in
                //Create Entity for the video
                let videoEntity = Entity()
                let url = URL(string: "https://somevideo/135-87.m3u8")!
                let asset = AVURLAsset(url: url)
                let playerItem = AVPlayerItem(asset: asset)
               
                 //create a videoMaterial
                let material = VideoMaterial(avPlayer: player)
                
                //Made a Sphere with the videoEntity and asign the videoMaterial to it
                videoEntity.components.set(ModelComponent(mesh: .generateSphere(radius: 1E3), materials: [material]))
                
                //adjust the properties of the videoEntity(Sphere) if needed
                videoEntity.scale = .init(x: 1, y: 1, z: -1)
                videoEntity.transform.translation += SIMD3<Float>(0.0, 10.0, 0.0)
                
                let angle = Angle.degrees(90)
                let rotation = simd_quatf(angle: Float(angle.radians), axis: .init(x: 0, y: 0, z: 0))
                
                videoEntity.transform.rotation = rotation
                
                //add VideoEntity to realityView
                content.add(videoEntity)
                
                //start the VideoPlayer
                player.replaceCurrentItem(with: playerItem)
                player.play()
            }
}

result displaying immersive view for Home Tab

swift swiftui avplayer realitykit visionos
1个回答
0
投票

您可以通过

RealityView<A>(make: (inout RealityViewContent, RealityViewAttachments) async -> Void, update: ((inout RealityViewContent, RealityViewAttachments) -> Void)?, attachments: () -> A)

并利用

update
添加/删除
ProgressView
attachments
来定义
ProgressView

struct ImmersiveMeditationPlayerView: View {
    //Get a dynamic URL
    @Binding var url: URL
    @State private var videoHelper: PlayerHelper = .init()
    @State private var isReady: Bool = false
    var body: some View {
        VStack {
            RealityView { content, attachments in
                content.add(videoHelper.videoEntity)
            } update: { content, attachments in
                switch isReady {
                case true:
                    if let progress = attachments.entity(for: ViewEntity.progress){
                        
                        videoHelper.videoEntity.removeChild(progress)
                    }
                case false:
                    if let progress = attachments.entity(for: ViewEntity.progress){
                        progress.position = [-0.5,0,0]
                        videoHelper.videoEntity.addChild(progress)
                    }
                }
                
            } attachments: {
                Attachment(id: ViewEntity.progress) {
                    ProgressView("Preparing Video")
                }
            }
            //Play the video and get status updates when url changes
            .task(id: url) {
                isReady = false
                try? await Task.sleep(for: .seconds(3))
                videoHelper.playURL(url: url)
                for await status in videoHelper.player.listenStatus() {
                    isReady = status
                }
            }
        }
    }
    enum ViewEntity: String {
        case progress
    }
}

我对“Helper”的定义如下。

@Observable
final class PlayerHelper {
    //Put the player somehwere that can be accessed by both the make and update
    let player: AVPlayer
    //Put the entity somehwere that can be accessed by both the make and update
    let videoEntity: Entity

    init() {
        //Set player
        let player = AVPlayer()
        self.player = player
        //SetEntity
        videoEntity = {
            //Create Entity for the video
            let videoEntity = Entity()
            //create a videoMaterial
            let material = VideoMaterial(avPlayer: player)
            
            //Made a Sphere with the videoEntity and asign the videoMaterial to it
            videoEntity.components.set(ModelComponent(mesh: .generateSphere(radius: 0.3), materials: [material]))
            
            //adjust the properties of the videoEntity(Sphere) if needed
            videoEntity.scale = .init(x: 0.5, y: 0.5, z: 0.5)
            //videoEntity.transform.translation += SIMD3<Float>(0.0, 10.0, 0.0)
            
            //let angle = Angle.degrees(90)
            //let rotation = simd_quatf(angle: Float(angle.radians), axis: .init(x: 0, y: 0, z: 0))
            
            //videoEntity.transform.rotation = rotation
            return videoEntity
        }()
        
        
    }
    //Update player
    func playURL(url: URL) {
        player.rate = 0
        let asset = AVURLAsset(url: url)
        let playerItem = AVPlayerItem(asset: asset)
        //start the VideoPlayer
        player.replaceCurrentItem(with: playerItem)
        player.play()
    }
    
}

您需要的其余代码只是一些方便的代码。

#Preview {
    ImmersiveMeditationPlayerView(url: .constant(URL(string: "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8")!))
}

extension AVPlayer {
    func listenStatus() -> AsyncStream<Bool> {
        AsyncStream  { continuation in
            continuation.yield(rate != 0)
            let observation = self.observe(\.rate) { player, newStatus in
                print(" kvo")
                continuation.yield(newStatus.newValue != 0)
            }
            continuation.onTermination = { _ in
                observation.invalidate()
                
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.