AVQueuePlayer观察者仅对首个视频生效问题求助
问题分析与解决方案
问题根源
- 只给第一个
AVPlayerItem绑定了播放结束通知,后续AVQueuePlayer自动切换的新currentItem没被监听,所以只有第一个视频结束能触发回调。 - 原代码里的
player.seek(to: .zero)是错误操作,这会把刚播放完的视频重置到开头,而不是推进到下一个视频。 - 直接在
init里给@State属性赋值不符合SwiftUI规范,容易引发状态异常。
修改后的完整代码
import SwiftUI import AVKit struct ExercisesPlay: View { @State private var urls: [URL] @State private var pauses: [Int] @State private var player: AVQueuePlayer @State private var showToast: Bool = false // 记录当前播放索引,匹配对应的暂停时长 @State private var currentIndex: Int = 0 init(urls: [URL], pauses: [Int]) { self._urls = State(initialValue: urls) self._pauses = State(initialValue: pauses) let playerItems = urls.map { AVPlayerItem(url: $0) } self._player = State(initialValue: AVQueuePlayer(items: playerItems)) } var body: some View { VideoPlayer(player: player) .onAppear { player.play() addPlayEndObserver() } .onDisappear { removePlayEndObserver() player.pause() player.removeAllItems() } .toast(message: LocalizedStringKey("Pause").stringValue() ?? "", isShowing: $showToast, duration: Toast.long) } private func addPlayEndObserver() { // 监听所有AVPlayerItem的播放结束事件,而非单个item NotificationCenter.default.addObserver( self, selector: #selector(handlePlayerItemDidEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil ) } private func removePlayEndObserver() { NotificationCenter.default.removeObserver( self, name: .AVPlayerItemDidPlayToEndTime, object: nil ) } @objc private func handlePlayerItemDidEnd(_ notification: Notification) { // 只处理当前播放器的currentItem结束事件 guard let playedItem = notification.object as? AVPlayerItem, playedItem == player.currentItem else { return } showToast.toggle() // 检查是否还有对应的暂停时长 guard currentIndex < pauses.count else { // 无暂停时长,直接播放下一个 player.play() currentIndex += 1 return } let pauseDuration = pauses[currentIndex] player.pause() DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(pauseDuration)) { currentIndex += 1 player.play() } } } // 补充Toast组件的简易实现(如果你的项目已有可忽略) enum Toast { static let long: TimeInterval = 3.0 } extension View { func toast(message: String, isShowing: Binding<Bool>, duration: TimeInterval) -> some View { self.overlay( Group { if isShowing.wrappedValue { Text(message) .padding() .background(Color.black.opacity(0.7)) .foregroundColor(.white) .cornerRadius(8) .transition(.opacity) .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + duration) { isShowing.wrappedValue = false } } } } .animation(.easeInOut, value: isShowing.wrappedValue) ) } } extension LocalizedStringKey { func stringValue() -> String? { let key = String(describing: self) return NSLocalizedString(key, comment: "") } }
关键修改点说明
- 通知监听范围调整:把监听对象设为
nil,这样能捕获所有AVPlayerItem的结束事件,再通过判断过滤出当前播放器的事件,避免漏监后续视频。 - 播放逻辑修正:去掉错误的
seek(to: .zero),AVQueuePlayer在当前item播放完后会自动停住,调用play()就会播放下一个item。 - 状态管理规范:用
_属性 = State(initialValue: ...)的正确方式初始化@State属性,避免状态异常。 - 索引跟踪:新增
currentIndex变量,确保每个视频对应的暂停时长准确匹配。 - 资源清理:在视图消失时清空播放器所有item,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Lamoichenzio
相关产品推荐
相关产品推荐

