我正在尝试在 SwiftUI 中使用 UIVideoEditorController,但遇到一个问题,点击“保存”后,它不会触发使用编辑的视频路径调用委托方法,并且还会消除UIVideoEditorController 直到返回到根视图控制器。我该如何解决这个问题?
这是我的 UIViewControllerRepresentable 版本的 UIVideoEditorController:
struct VideoEditor: UIViewControllerRepresentable {
let source: URL
let maxSeconds: TimeInterval
let onSaved: (URL) -> Void
@Environment(\.dismiss) private var dismiss
func makeUIViewController(context: Context) -> UIVideoEditorController {
let editor = UIVideoEditorController()
editor.videoPath = source.absoluteString
editor.videoMaximumDuration = maxSeconds
editor.videoQuality = .typeHigh
editor.delegate = context.coordinator
return editor
}
func updateUIViewController(_ uiViewController: UIVideoEditorController, context: Context) {}
func makeCoordinator() -> Coordinator { Coordinator(videoEditor: self) }
class Coordinator: NSObject, UINavigationControllerDelegate, UIVideoEditorControllerDelegate {
private let videoEditor: VideoEditor
init(videoEditor: VideoEditor) {
self.videoEditor = videoEditor
}
func videoEditorController(
_ editor: UIVideoEditorController,
didSaveEditedVideoToPath editedVideoPath: String
) {
let url = URL(fileURLWithPath: editedVideoPath)
videoEditor.onSaved(url)
videoEditor.dismiss()
}
}
}
我的使用方法如下:
struct ContentView: View {
let videoToEdit: URL
@State private var presentingFirstCover = false
@State private var presentingEditor = false
var body: some View {
VStack {
Button("Present first cover") { presentingFirstCover = true }
}
.fullScreenCover(isPresented: $presentingFirstCover) {
Button("Present editor") { presentingEditor = true }
.fullScreenCover(isPresented: $presentingEditor) {
VideoEditor(source: fullVideo, maxSeconds: 20) {
print("saved edited video to: \($0)")
}
}
}
}
我想通了!事实证明问题是:
source.path
,而不是source.absoluteString
),这导致了一个问题:它不会保存修剪后的视频,而是会取消。videoEditorControllerDidCancel
的 UIVideoEditorControllerDelegate
方法,我需要自己解雇视图控制器。如果没有定义这个方法,UIVideoEditorController 就没有办法解散自己,只能解散回根目录,所以这就是它所做的。这是我最终的工作解决方案:
import SwiftUI
struct VideoEditor: UIViewControllerRepresentable {
let source: URL
let maxSeconds: TimeInterval
let onSaved: (URL) -> Void
@Environment(\.dismiss) private var dismiss
func makeUIViewController(context: Context) -> UIVideoEditorController {
let editor = UIVideoEditorController()
editor.videoPath = source.path
editor.videoMaximumDuration = maxSeconds
editor.videoQuality = .typeHigh
editor.delegate = context.coordinator
return editor
}
func updateUIViewController(_ uiViewController: UIVideoEditorController, context: Context) {}
func makeCoordinator() -> Coordinator { Coordinator(videoEditor: self) }
class Coordinator: NSObject, UINavigationControllerDelegate, UIVideoEditorControllerDelegate {
private let videoEditor: VideoEditor
private var savedEditedVideo = false
init(videoEditor: VideoEditor) {
self.videoEditor = videoEditor
}
func videoEditorController(
_ editor: UIVideoEditorController,
didSaveEditedVideoToPath editedVideoPath: String
) {
defer { videoEditor.dismiss() }
// For some reason, this delegate method is always called twice.
// So, we must make sure we only call onSaved the first time this delegate method is called.
if savedEditedVideo { return }
let urlSavedTo = URL(fileURLWithPath: editedVideoPath)
videoEditor.onSaved(urlSavedTo)
savedEditedVideo = true
}
func videoEditorControllerDidCancel(_ editor: UIVideoEditorController) {
videoEditor.dismiss()
}
func videoEditorController(
_ editor: UIVideoEditorController, didFailWithError error: any Error
) {
print("Failed to edit video \(videoEditor.source), with error \(error)")
videoEditor.dismiss()
}
}
}
尽管付出了所有这些努力,但遗憾的是我无法使用我的解决方案,因为导出的视频质量不够高,即使设置为
.typeHigh
(也只有 480p)。
相反,我必须使用
UIImagePickerController
直接从相机胶卷中选择视频并修剪它,因为使用该视图控制器,您可以将 videoExportPreset
设置为 AVAssetExportPresetPassthrough
以使其保持高清质量,如果所选视频是高清的。