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

SwiftUI视图出现时播放视频报错,如何等待资源加载后播放?

解决AVPlayer主线程阻塞问题:等待资源加载完成再播放

你遇到的错误是因为远程视频资源未加载完成时就调用了play(),导致主线程同步查询未就绪的媒体属性(PreferredTransform)被阻塞,慢速网络下问题会更突出。解决核心是监听AVPlayerItem的加载状态,等资源真正准备好后再触发播放。

核心思路

  1. 改用AVPlayerItem管理远程媒体资源,替代直接用URL初始化AVPlayer的方式
  2. 监听AVPlayerItem的status属性,仅当状态变为.readyToPlay时执行播放
  3. 避免视图刚出现时强制播放,让资源加载完成后自动触发播放逻辑

修改后的完整代码

import SwiftUI
import AVKit
import Combine

struct MainVideoPlayer: View {
    let url: URL
    @State private var player: AVPlayer?
    @State private var cancellables = Set<AnyCancellable>()
    
    var body: some View {
        ZStack {
            VideoPlayer(player: $player)
        }
        .onAppear {
            setupPlayer()
        }
        .onDisappear {
            // 清理资源,避免内存泄漏
            player?.pause()
            player = nil
            cancellables.removeAll()
        }
    }
    
    private func setupPlayer() {
        let playerItem = AVPlayerItem(url: url)
        
        // 用Combine监听playerItem的状态变化
        playerItem.publisher(for: \.status)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] status in
                guard let self = self else { return }
                switch status {
                case .readyToPlay:
                    // 资源准备完成,开始播放
                    self.player?.play()
                case .failed:
                    // 处理加载失败场景
                    if let error = playerItem.error {
                        print("视频加载失败: \(error.localizedDescription)")
                    }
                case .unknown:
                    // 资源加载中,等待即可
                    break
                @unknown default:
                    break
                }
            }
            .store(in: &cancellables)
        
        // 初始化player并关联playerItem
        player = AVPlayer(playerItem: playerItem)
    }
}

struct VideoPlayer: UIViewControllerRepresentable {
    @Binding var player: AVPlayer?
    
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let controller = AVPlayerViewController()
        controller.showsPlaybackControls = false
        controller.videoGravity = .resizeAspectFill
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
        // 当player实例变化时更新控制器关联的player
        uiViewController.player = player
    }
}

关键修改点说明

  • AVPlayerItem管理资源:通过AVPlayerItem可精准监听媒体加载状态,避免直接操作AVPlayer导致的主线程同步阻塞
  • Combine状态监听:利用Combine框架监听status属性变化,确保在主线程处理状态回调,保证UI操作安全
  • 资源清理:在视图消失时暂停播放并释放player,避免内存泄漏
  • 错误处理:添加加载失败的逻辑分支,方便后续扩展用户提示

额外优化建议

  • 可在资源加载中(status为.unknown时)添加加载指示器,提升用户体验
  • 若需支持缓存,可通过AVAssetResourceLoader实现自定义资源加载逻辑

内容的提问来源于stack exchange,提问作者Ahmed Zaidan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:20:12