在沉浸式视图中显示加载动画,直到 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()
}
}
您可以通过
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()
}
}
}
}