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

Swift使用ReplayKit录屏保存到Documents后无法找到视频URL的问题

问题根因
  • 首先是stopRecording(withOutput:)传入的参数错误:该方法要求传入完整的目标视频文件URL(包含文件名和后缀),你当前直接传入Documents目录的URL,系统无法确定文件存储名称,不会生成有效的录制文件。
  • 其次是getDocument方法的逻辑完全错误:FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)返回的是系统匹配到的所有Documents目录的路径列表,paths[0]是Documents文件夹本身的URL,不是文件夹下的视频文件,将文件夹路径传给AVPlayer自然无法正常播放。
  • 还有异步逻辑和状态同步问题:stopRecording的保存操作是异步执行的,你当前在调用方法后立刻修改状态,此时录制文件还没完成写入,提前获取URL必然失效;且你的ScreenRecording是ObservableObject但没有使用@Published修饰需要响应的属性,界面无法感知录制完成的状态。
修复方案

核心代码修改如下:

class ScreenRecording: ObservableObject {
    // 用@Published修饰,录制完成后界面能感知状态变化
    @Published var recording: Bool = false
    // 存储最新录制完成的视频文件URL
    @Published var recordedVideoURL: URL?
    private let screenRecorder = RPScreenRecorder.shared()
    
    static let singleton = ScreenRecording()
    private init() {}
    
    func startRecording() {
        if !screenRecorder.isRecording && RPScreenRecorder.shared().isAvailable {
            screenRecorder.isMicrophoneEnabled = true
            screenRecorder.isCameraEnabled = true
            screenRecorder.startRecording { err in
                guard err == nil else {
                    print("Failed to start recording...")
                    return
                }
                DispatchQueue.main.async {
                    self.recording = true
                    print("Recording starts!")
                }
            }
        } else {
            print("Trying to start recording when a recording is already happening!")
        }
    }
    
    func stopRecording() {
        if screenRecorder.isRecording {
            // 生成带文件名和后缀的完整输出URL,这里用时间戳避免文件名重复
            let outputURL = getDocumentDirectoryURL().appendingPathComponent("\(Date().timeIntervalSince1970).mp4")
            screenRecorder.stopRecording(withOutput: outputURL) { err in
                guard err == nil else {
                    print("An error has ocurred: \(String(describing: err))")
                    return
                }
                DispatchQueue.main.async {
                    // 保存录制完成的文件URL
                    self.recordedVideoURL = outputURL
                    self.recording = false
                    print("Recording stops! File saved at \(outputURL.path)")
                }
            }
        } else {
            print("Trying to stop recording when NO recording is initiated!")
        }
    }
    
    func getDocumentDirectoryURL() -> URL {
        let documentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        print("Document dir path is: \(documentDirURL.path)")
        return documentDirURL
    }
}

对应修改ContentView的播放逻辑,判断文件存在再加载:

if showVideo, let videoURL = screenRecording.recordedVideoURL {
    VideoPlayer(player: AVPlayer(url: videoURL))
        .frame(height: 400)
        .onAppear {
            print("Play URL: \(videoURL.path)")
        }
} else if showVideo {
    Text("暂无可播放的录制视频,请先完成录制")
        .frame(height: 400)
}
额外注意事项
  • 录制过程中不要提前退出页面,避免文件写入中断
  • 如果需要列举Documents目录下所有历史录制文件,可以用FileManager.default.contentsOfDirectory(at:includingPropertiesForKeys:options:)方法遍历目录下的视频文件
  • 注意申请麦克风、摄像头权限,在Info.plist中添加对应的权限描述

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 06:36:04