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
相关产品推荐
相关产品推荐

