You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVQueuePlayer观察者仅对首个视频生效问题求助

问题分析与解决方案

问题根源

  1. 只给第一个AVPlayerItem绑定了播放结束通知,后续AVQueuePlayer自动切换的新currentItem没被监听,所以只有第一个视频结束能触发回调。
  2. 原代码里的player.seek(to: .zero)是错误操作,这会把刚播放完的视频重置到开头,而不是推进到下一个视频。
  3. 直接在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 17:48:01