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

SwiftUI中PHPicker选取视频后VideoPlayer无法播放问题

问题定位

你的代码存在3个直接导致播放失败的问题:

  • 播放逻辑存在低级语法错误:VideoPlayer初始化时传入的是URL类型本身,而非你解包得到的实际路径变量,AVPlayer初始化时根本没有拿到有效资源地址。
  • 缺失相册资源安全权限处理:PHPicker返回的视频URL是系统安全域限制的临时URL,直接调用FileManager执行拷贝操作会因为权限不足失败,你用try?静默吞掉了所有拷贝错误,完全没有感知到文件根本没有复制到临时目录。
  • 强制修改文件后缀:你给所有选中的视频强制拼接.mp4后缀,但系统相册内的视频可能是mov、m4v等其他封装格式,后缀和实际文件格式不匹配也会导致播放失败。
修复方案

1. 修正PHPicker视频选取、拷贝逻辑

不要直接拿返回的URL做文件操作,先申请安全域访问权限,保留原文件后缀,不要吞掉文件操作的错误:

注意:使用UTType做资源类型校验需要在文件顶部引入依赖:import UniformTypeIdentifiers

func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
    picker.dismiss(animated: true)
    guard let pickResult = results.first else { return }
    // 校验资源是否为视频类型
    guard pickResult.itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) else { return }

    pickResult.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { [weak self] originURL, loadError in
        guard let self = self, loadError == nil, let sourceURL = originURL else {
            debugPrint("视频资源加载失败:\(loadError?.localizedDescription ?? "未知错误")")
            return
        }
        // 申请安全作用域访问权限
        guard sourceURL.startAccessingSecurityScopedResource() else {
            debugPrint("相册资源访问权限获取失败")
            return
        }
        // 作用域结束后自动释放权限
        defer { sourceURL.stopAccessingSecurityScopedResource() }

        DispatchQueue.main.async {
            do {
                let fileID = UUID().uuidString
                // 保留原始文件后缀,不要强制指定mp4
                let pathExt = sourceURL.pathExtension
                let targetURL = URL(fileURLWithPath: NSTemporaryDirectory())
                    .appendingPathComponent(fileID)
                    .appendingPathExtension(pathExt)
                // 临时路径存在同名文件先删除,避免拷贝报错
                if FileManager.default.fileExists(atPath: targetURL.path) {
                    try FileManager.default.removeItem(at: targetURL)
                }
                // 执行拷贝,捕获错误
                try FileManager.default.copyItem(at: sourceURL, to: targetURL)
                // 将有效URL同步给视图模型
                self.providerRoomSummitViewModel.roomIntroVideoURL = targetURL
            } catch {
                debugPrint("视频拷贝到临时目录失败:\(error.localizedDescription)")
            }
        }
    }
}

2. 修正VideoPlayer调用代码

传入解包得到的实际URL变量,而非URL类型本身:

if let playableURL = providerRoomSummitViewModel.roomIntroVideoURL {
    VideoPlayer(player: AVPlayer(url: playableURL))
        .frame(height: 280) // 根据页面需求设置播放器高度
}

开发提示:文件读写、权限类操作不要用try?静默忽略错误,通过do-catch打印错误信息可以快速定位90%以上的路径、权限、文件冲突问题。临时目录的文件会在系统存储空间不足时被自动清理,无需手动删除;如果需要长期保存选中的视频,可以将文件拷贝到应用沙盒的Documents或Library目录下。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 16:42:16