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

macOS SwiftUI应用中AVPlayer加载新剧集后意外自动暂停问题

macOS SwiftUI应用中AVPlayer加载新剧集后意外自动暂停问题

嘿,我之前做macOS播客应用的时候也踩过AVPlayer自动暂停的坑,结合你贴的代码片段,大概率是这几个细节没处理好,咱们一步步来解决:

先帮你补全并梳理下你的现有代码(你贴的片段里有小疏漏)

首先看你ContentView里的播放按钮代码应该是打错了,playerViewMode...应该是playerViewModel,补全后的完整片段是这样的:

import SwiftUI
import AVFoundation

struct ContentView: View {
    @StateObject private var playerViewModel = PlayerViewModel()

    var body: some View {
        VStack {
            // 剧集列表
            List(playerViewModel.episodeURLs.indices, id: \.self) { index in
                Button(action: {
                    playerViewModel.loadEpisode(at: index)
                }) {
                    Text("Episode \(index + 1)")
                }
            }

            // 播放/暂停按钮
            Button(action: playerViewModel.togglePlayPause) {
                Image(systemName: playerViewModel.isPlaying ? "pause.circle.fill" : "play.circle.fill")
                    .font(.largeTitle)
            }
        }
        .padding()
    }
}

而你的PlayerViewModel大概率是类似这样的实现(我猜的,因为你没贴全),这也是问题的高发区:

class PlayerViewModel: ObservableObject {
    @Published var episodeURLs: [URL] = [/* 你的剧集URL数组 */]
    @Published var isPlaying = false
    private var player: AVPlayer?

    func loadEpisode(at index: Int) {
        let url = episodeURLs[index]
        let playerItem = AVPlayerItem(url: url)
        player = AVPlayer(playerItem: playerItem)
        // 这里很可能没处理item的就绪状态,也没主动触发播放
    }

    func togglePlayPause() {
        guard let player = player else { return }
        if isPlaying {
            player.pause()
        } else {
            player.play()
        }
        isPlaying.toggle()
    }
}

问题到底出在哪?

  1. 每次加载新剧集都新建AVPlayer实例:新创建的AVPlayer默认处于暂停状态,你如果没主动调用play(),自然会停住;
  2. 没监听AVPlayerItem的就绪状态:如果直接在加载item后调用play(),item可能还没准备好,播放请求会被系统忽略;
  3. macOS音频会话未激活:如果没有正确激活音频会话,系统可能会自动暂停你的播放请求。

具体的修复方案

1. 激活macOS音频会话

在PlayerViewModel的初始化方法里添加音频会话激活逻辑,确保系统允许你的应用播放音频:

class PlayerViewModel: ObservableObject {
    @Published var episodeURLs: [URL] = [/* 你的剧集URL */]
    @Published var isPlaying = false
    private var player: AVPlayer?
    private var playerItemObservation: NSKeyValueObservation? // 用来监听item状态

    init() {
        // 激活音频会话
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
            try AVAudioSession.sharedInstance().setActive(true)
        } catch {
            print("音频会话激活失败: \(error.localizedDescription)")
        }
    }

    // ... 其他方法
}

2. 复用AVPlayer实例,替换AVPlayerItem而非新建Player

每次新建Player不仅会导致状态重置,还会浪费资源,复用同一个Player只替换item更稳定:

func loadEpisode(at index: Int) {
    let url = episodeURLs[index]
    let playerItem = AVPlayerItem(url: url)

    // 监听AVPlayerItem的就绪状态,确保item准备好后再处理播放
    playerItemObservation = playerItem.observe(\.status, options: [.new]) { [weak self] item, _ in
        guard let self = self else { return }
        switch item.status {
        case .readyToPlay:
            // 如果之前处于播放状态,加载新item后自动继续播放
            if self.isPlaying {
                self.player?.play()
            }
        case .failed:
            print("剧集加载失败: \(item.error?.localizedDescription ?? "未知错误")")
        case .unknown:
            break
        @unknown default:
            break
        }
    }

    // 复用现有Player,只替换item
    if let existingPlayer = player {
        existingPlayer.replaceCurrentItem(with: playerItem)
    } else {
        // 第一次加载时创建Player
        player = AVPlayer(playerItem: playerItem)
    }

    // 如果当前是播放状态,先尝试触发播放(item就绪后会自动续上)
    if isPlaying {
        player?.play()
    }
}

3. 同步播放状态与Player的真实状态

原来的togglePlayPause方法只依赖自己的isPlaying变量,容易出现状态不同步,改成用Player的timeControlStatus来判断真实状态:

func togglePlayPause() {
    guard let player = player else { return }
    switch player.timeControlStatus {
    case .playing:
        player.pause()
        isPlaying = false
    case .paused, .waitingToPlayAtSpecifiedRate:
        player.play()
        isPlaying = true
    @unknown default:
        break
    }
}

最后再测试下

按照上面的修改后,加载新剧集时:

  • 如果之前是播放状态,新item准备好后会自动继续播放;
  • 如果之前是暂停状态,新item加载后也会保持暂停,完全符合你的需求。

备注:内容来源于stack exchange,提问作者THEBONESMITH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 09:23:06