SwiftUI中如何在UIImagePickerController内编辑录制的视频?
实现视频录制后自定义裁剪并保存的方案
你的现有代码依赖UIImagePickerController的默认编辑功能,但其视频裁剪交互灵活性不足。要实现「录制视频→调整长度→点击Use Video保存」的流程,需要自定义裁剪界面并结合AVFoundation处理视频裁剪,具体实现如下:
步骤1:调整视频录制逻辑,传递视频URL到主界面
修改ContentView和RecordVideoPicker,让录制完成后将视频URL传回主界面,而非直接保存到相册:
struct ContentView: View { @State private var isShowCamera = false @State private var selectedVideoURL: URL? var body: some View { VStack(spacing: 20) { Button { isShowCamera.toggle() } label: { Text("Record Video") .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } // 选中视频后展示自定义裁剪界面 if let videoURL = selectedVideoURL { VideoCropView(videoURL: videoURL, onSave: { croppedURL in // 保存裁剪后的视频到相册 if let path = croppedURL?.path, UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path) { UISaveVideoAtPathToSavedPhotosAlbum(path, nil, nil, nil) } selectedVideoURL = nil }, onCancel: { selectedVideoURL = nil }) } } .sheet(isPresented: $isShowCamera) { RecordVideoPicker(sourceType: .camera) { url in selectedVideoURL = url } } } } struct RecordVideoPicker: UIViewControllerRepresentable { @Environment(\.presentationMode) private var presentationMode var sourceType: UIImagePickerController.SourceType = .camera let onVideoRecorded: (URL?) -> Void // 回调传递录制好的视频URL func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} func makeUIViewController(context: Context) -> UIViewController { let mediaUI = UIImagePickerController() mediaUI.sourceType = sourceType mediaUI.mediaTypes = [kUTTypeMovie as String] mediaUI.allowsEditing = false // 关闭默认编辑,使用自定义裁剪界面 mediaUI.delegate = context.coordinator return mediaUI } final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { var parent: RecordVideoPicker init(_ parent: RecordVideoPicker) { self.parent = parent } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { parent.presentationMode.wrappedValue.dismiss() guard let url = info[.mediaURL] as? URL else { parent.onVideoRecorded(nil) return } parent.onVideoRecorded(url) } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { parent.presentationMode.wrappedValue.dismiss() parent.onVideoRecorded(nil) } } func makeCoordinator() -> Coordinator { Coordinator(self) } }
步骤2:创建自定义视频裁剪界面
实现带滑动条的裁剪View,用AVPlayer播放视频,通过滑块控制裁剪的起始和结束时间:
import AVFoundation import AVKit struct VideoCropView: View { let videoURL: URL let onSave: (URL?) -> Void let onCancel: () -> Void @State private var player: AVPlayer? @State private var startTime: Double = 0.0 @State private var endTime: Double = 0.0 @State private var duration: Double = 0.0 var body: some View { VStack(spacing: 16) { // 视频播放器 VideoPlayer(player: player) .frame(height: 300) .cornerRadius(8) // 时间控制滑块组 HStack { Text(String(format: "%.1f", startTime)) Slider(value: $startTime, in: 0...endTime, step: 0.1) Text(String(format: "%.1f", endTime)) Slider(value: $endTime, in: startTime...duration, step: 0.1) } .padding(.horizontal) // 操作按钮 HStack(spacing: 20) { Button("Cancel") { onCancel() } .padding() .background(Color.gray) .foregroundColor(.white) .cornerRadius(8) Button("Use Video") { cropVideo() } .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) .disabled(startTime >= endTime) // 起始时间大于结束时间时禁用按钮 } } .onAppear { setupPlayer() } .onDisappear { player?.pause() } } private func setupPlayer() { let player = AVPlayer(url: videoURL) self.player = player // 获取视频总时长 let asset = AVURLAsset(url: videoURL) duration = CMTimeGetSeconds(asset.duration) endTime = duration } private func cropVideo() { let asset = AVURLAsset(url: videoURL) guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else { onSave(nil) return } // 设置裁剪时间范围 let startCMTime = CMTimeMakeWithSeconds(startTime, preferredTimescale: 600) let endCMTime = CMTimeMakeWithSeconds(endTime, preferredTimescale: 600) exportSession.timeRange = CMTimeRange(start: startCMTime, end: endCMTime) // 设置输出路径(临时目录) let tempDir = NSTemporaryDirectory() let outputFilename = UUID().uuidString + ".mov" let outputURL = URL(fileURLWithPath: tempDir).appendingPathComponent(outputFilename) exportSession.outputURL = outputURL exportSession.outputFileType = .mov // 执行视频导出 exportSession.exportAsynchronously(completionHandler: { DispatchQueue.main.async { if exportSession.status == .completed { onSave(outputURL) } else { onSave(nil) } } }) } }
核心说明
- 关闭
UIImagePickerController的默认编辑功能,改用自定义裁剪界面,完全匹配你需要的「编辑条调整长度」交互逻辑。 - 使用AVFoundation的
AVAssetExportSession处理视频裁剪,支持精确控制起始和结束时间。 - 裁剪后的视频先保存到系统临时目录,再通过
UISaveVideoAtPathToSavedPhotosAlbum保存到系统相册。 - 「Use Video」按钮会在起始时间大于等于结束时间时自动禁用,避免无效操作。
内容的提问来源于stack exchange,提问作者Yung Sheng Zeng
相关产品推荐
相关产品推荐

