SwiftUI中AVPlayer仅连接第二屏幕时正常播放问题求助
问题:iPad端未连接AirPlay时AVPlayer无法播放视频,连接后正常
我在SwiftUI中开发演示功能,需求是iPad端显示幻灯片,同时通过AirPlay在第二屏幕同步展示独立视图。通过screenDidConnectPublisher监听第二屏幕连接:
ContentView() .onReceive(screenDidConnectPublisher, perform: screenDidConnect)
iPad端通过PresentView展示包含图片和视频的幻灯片;连接第二屏幕后,PresentScreenView同步展示当前选中的幻灯片。
问题:未连接第二屏幕时,iPad端的AVPlayer有时无法播放视频,仅显示空白界面;连接第二屏幕后,两端视频均可正常播放。
Present View(iPad端)代码
ZStack { if let media = mediaCacheManager.mediaCache[appModel.currentSlide] { if let player = media as? AVPlayer { PlayerView(player: player) .aspectRatio(16 / 10, contentMode: .fit) .frame(width: UIScreen.main.bounds.width * 0.8) .background(Color.gray.opacity(0.2)) .clipShape(RoundedRectangle(cornerRadius: 40)) .overlay( RoundedRectangle(cornerRadius: 40) .stroke(Color.gray.opacity(0.5), lineWidth: 1) ) .onDisappear { player.pause() } } else if let image = media as? Image { image .resizable() .scaledToFit() .frame(width: UIScreen.main.bounds.width * 0.8) .background(Color.gray.opacity(0.2)) .clipShape(RoundedRectangle(cornerRadius: 40)) .overlay( RoundedRectangle(cornerRadius: 40) .stroke(Color.gray.opacity(0.5), lineWidth: 1) ) .padding(.vertical, 10) } } }
PresentScreenView(第二屏幕端)代码
// // PresentScreenView.swift // Interactive // // Created by Vikrant Gurav on 07/11/2024. // import SwiftUI import AVKit struct PresentScreenView: View { @EnvironmentObject private var appModel: AppModel @EnvironmentObject var currentPresentation: CurrentPresentation @EnvironmentObject var mediaCacheManager: MediaCacheManager @State var currentMedia: Image? var body: some View { if let selectedPresentation = currentPresentation.currentPresentation { if appModel.controlScreenSlide { TabView(selection: $appModel.currentSlideScreen) { ForEach(selectedPresentation.slides.indices, id: \.self) { index in if let media = mediaCacheManager.mediaCache[index] { if let player = media as? AVPlayer { VideoPlayer(player: player) .frame( maxHeight: .infinity) .ignoresSafeArea(.all) } } } } .opacity(currentMedia == nil ? 1 : 0) .overlay { if let currentMedia { currentMedia .resizable() .scaledToFill() } } .ignoresSafeArea(.all) .task(id: appModel.currentSlideScreen, { let newValue = appModel.currentSlideScreen if mediaCacheManager.typeCache[newValue] == .image { currentMedia = mediaCacheManager.mediaCache[newValue] as? Image } else { currentMedia = nil } }) .frame(maxWidth: .infinity , maxHeight: .infinity) .tabViewStyle(.page(indexDisplayMode: .never)) } else { HStack{ Text("Enable Screen Control") .font(.largeTitle) .fontWeight(.bold) .frame(maxWidth: .infinity, maxHeight: .infinity) } .overlay { if let currentMedia { currentMedia .resizable() .scaledToFill() } } .ignoresSafeArea(.all) .frame(maxWidth: .infinity , maxHeight: .infinity) } } } }
解决思路
主动触发AVPlayer播放
未连接第二屏幕时,缓存的AVPlayer可能未自动启动播放。自定义PlayerView时,在视图更新阶段检查player状态并主动调用播放:struct PlayerView: UIViewRepresentable { let player: AVPlayer func makeUIView(context: Context) -> UIView { let view = UIView() let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = view.bounds playerLayer.videoGravity = .resizeAspectFit view.layer.addSublayer(playerLayer) context.coordinator.playerLayer = playerLayer return view } func updateUIView(_ uiView: UIView, context: Context) { context.coordinator.playerLayer?.frame = uiView.bounds if player.rate == 0 && player.currentItem?.status == .readyToPlay { player.play() } } func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator { var playerLayer: AVPlayerLayer? } }同时在
PlayerView的onAppear中恢复播放,避免onDisappear的暂停操作导致状态残留:PlayerView(player: player) .onAppear { if player.currentItem?.status == .readyToPlay { player.play() } } // 其他修饰符...检查缓存中AVPlayer的有效性
从缓存获取AVPlayer时,验证currentItem的加载状态,若状态异常则重新初始化并更新缓存:if let player = media as? AVPlayer { if player.currentItem?.status != .readyToPlay { let newPlayer = AVPlayer(url: yourVideoURL) mediaCacheManager.mediaCache[appModel.currentSlide] = newPlayer PlayerView(player: newPlayer) // 修饰符... } else { PlayerView(player: player) // 修饰符... } }强制视图重建更新
给PlayerView绑定与当前幻灯片索引关联的id,确保切换幻灯片时视图完全重建,避免旧player状态干扰:PlayerView(player: player) .id(appModel.currentSlide) // 其他修饰符...
内容的提问来源于stack exchange,提问作者VMI
相关产品推荐
相关产品推荐

