SwiftUI实现视频选择与播放:UIKit代码转换遇阻求助
问题:UIKit视频选择播放代码转SwiftUI后代理函数失效
我正在开发一款相机应用,此前从未使用UIKit开发应用,但参考代码多为UIKit实现。我需要将一段实现视频选择与播放的UIKit代码转换为SwiftUI,自行编写的代码可编译通过,但代理函数无法正常工作。
原UIKit代码
VideoHelper工具类
static func startMediaBrowser( delegate: UIViewController & UINavigationControllerDelegate & UIImagePickerControllerDelegate, sourceType: UIImagePickerController.SourceType ) { guard UIImagePickerController.isSourceTypeAvailable(sourceType) else { return } let mediaUI = UIImagePickerController() mediaUI.sourceType = sourceType mediaUI.mediaTypes = [kUTTypeMovie as String] mediaUI.allowsEditing = true mediaUI.delegate = delegate delegate.present(mediaUI, animated: true, completion: nil) }
视频播放视图控制器
import AVKit import MobileCoreServices import UIKit class PlayVideoViewController: UIViewController { @IBAction func playVideo(_ sender: AnyObject) { VideoHelper.startMediaBrowser(delegate: self, sourceType: .savedPhotosAlbum) } } // MARK: - UIImagePickerControllerDelegate extension PlayVideoViewController: UIImagePickerControllerDelegate { func imagePickerController( _ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any] ) { guard let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String, mediaType == (kUTTypeMovie as String), let url = info[UIImagePickerController.InfoKey.mediaURL] as? URL else { return } dismiss(animated: true) { let player = AVPlayer(url: url) let vcPlayer = AVPlayerViewController() vcPlayer.player = player self.present(vcPlayer, animated: true, completion: nil) } } } // MARK: - UINavigationControllerDelegate extension PlayVideoViewController: UINavigationControllerDelegate { }
我尝试的SwiftUI代码
主视图
import SwiftUI import AVKit import MobileCoreServices import UIKit struct ContentView: View { @State private var isShowVideoLibrary = false @State private var image = UIImage() @State private var isShowCamara = false var body: some View { VStack { HStack{ Button { isShowVideoLibrary.toggle() } label: { Text("Play video") } } } .sheet(isPresented: $isShowVideoLibrary) { VideoPicker(sourceType: .photoLibrary) } } }
视频选择器
struct VideoPicker: UIViewControllerRepresentable { var sourceType: UIImagePickerController.SourceType = .photoLibrary @Environment(\.presentationMode) private var presentationMode func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPicker>) -> UIViewController { let mediaUI = UIImagePickerController() mediaUI.sourceType = sourceType mediaUI.mediaTypes = [kUTTypeMovie as String] mediaUI.allowsEditing = true mediaUI.delegate = context.coordinator return mediaUI } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { } final class Coordinator : NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate{ var parent: VideoPicker init(_ parent: VideoPicker) { self.parent = parent } private func imagePickerController( _ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) -> UIViewController { guard let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String, mediaType == (kUTTypeMovie as String), let url = info[UIImagePickerController.InfoKey.mediaURL] as? URL else { return AVPlayerViewController()} parent.presentationMode.wrappedValue.dismiss() let player = AVPlayer(url: url) let vcPlayer = AVPlayerViewController() vcPlayer.player = player return vcPlayer } } func makeCoordinator() -> Coordinator { Coordinator(self) } }
问题原因与修正方案
核心问题
- 代理方法定义错误:系统的
UIImagePickerControllerDelegate代理方法imagePickerController(_:didFinishPickingMediaWithInfo:)是无返回值的,你错误地给它添加了返回值UIViewController,还标记为private,导致系统无法识别并调用该方法。 - SwiftUI环境下的弹窗逻辑错误:在SwiftUI中不能直接在协调器里返回视图控制器,需要通过绑定值将视频URL传递到主视图,再用SwiftUI的弹窗组件展示播放器。
修正后的完整代码
主视图(ContentView)
import SwiftUI import AVKit struct ContentView: View { @State private var isShowVideoLibrary = false // 存储选中的视频URL,用于触发播放器弹窗 @State private var selectedVideoURL: URL? var body: some View { VStack { Button { isShowVideoLibrary.toggle() } label: { Text("Play video") .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } } // 视频选择器弹窗 .sheet(isPresented: $isShowVideoLibrary) { VideoPicker(sourceType: .photoLibrary) { url in selectedVideoURL = url } } // 视频播放器全屏弹窗 .fullScreenCover(item: $selectedVideoURL) { url in AVPlayerControllerRepresentable(player: AVPlayer(url: url)) } } } // 封装AVPlayerViewController为SwiftUI视图 struct AVPlayerControllerRepresentable: UIViewControllerRepresentable { let player: AVPlayer func makeUIViewController(context: Context) -> AVPlayerViewController { let playerVC = AVPlayerViewController() playerVC.player = player // 自动播放 player.play() return playerVC } func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {} }
视频选择器(VideoPicker)
import UIKit import MobileCoreServices struct VideoPicker: UIViewControllerRepresentable { let sourceType: UIImagePickerController.SourceType // 回调闭包,用于传递选中的视频URL let onVideoSelected: (URL) -> Void func makeUIViewController(context: Context) -> UIImagePickerController { let mediaUI = UIImagePickerController() mediaUI.sourceType = sourceType mediaUI.mediaTypes = [kUTTypeMovie as String] mediaUI.allowsEditing = true mediaUI.delegate = context.coordinator return mediaUI } func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { let parent: VideoPicker init(_ parent: VideoPicker) { self.parent = parent } // 修正:移除private修饰符,改为无返回值的正确代理方法 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let mediaType = info[.mediaType] as? String, mediaType == kUTTypeMovie as String, let url = info[.mediaURL] as? URL else { picker.dismiss(animated: true) return } // 关闭选择器,然后通过回调传递URL picker.dismiss(animated: true) { self.parent.onVideoSelected(url) } } // 处理取消选择的情况 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } } }
内容的提问来源于stack exchange,提问作者Yung Sheng Zeng
相关产品推荐
相关产品推荐

