SwiftUI中AVPlayer视图适配视频内容尺寸(消除全屏红色背景)
解决方法
步骤1:获取视频原生宽高比
先实现一个工具函数,从AVPlayer的资源中提取视频的宽高比例,这是让视图匹配视频尺寸的核心依据:
func getVideoAspectRatio(for player: AVPlayer) -> CGFloat? { guard let asset = player.currentItem?.asset else { return nil } return asset.tracks(withMediaType: .video).first.flatMap { track in let size = track.naturalSize.applying(track.preferredTransform) return abs(size.width / size.height) } }
步骤2:修改VideoPlayerUIView适配动态尺寸
调整UIViewControllerRepresentable的实现,取消强制全屏的设置,同时监听视频资源变化来同步宽高比:
struct VideoPlayerUIView: UIViewControllerRepresentable { let player: AVPlayer @Binding var aspectRatio: CGFloat? func makeUIViewController(context: Context) -> UIViewController { let controller = UIViewController() let playerLayer = AVPlayerLayer(player: player) playerLayer.videoGravity = .resizeAspect playerLayer.frame = controller.view.bounds controller.view.layer.addSublayer(playerLayer) // 监听视频资源变化,更新宽高比 player.addObserver(context.coordinator, forKeyPath: "currentItem", options: .new, context: nil) return controller } func updateUIViewController(_ uiViewController: UIViewController, context: Context) { guard let playerLayer = uiViewController.view.layer.sublayers?.first as? AVPlayerLayer else { return } playerLayer.player = player playerLayer.frame = uiViewController.view.bounds // 同步最新宽高比 aspectRatio = getVideoAspectRatio(for: player) } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject { let parent: VideoPlayerUIView init(_ parent: VideoPlayerUIView) { self.parent = parent } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "currentItem" { parent.aspectRatio = getVideoAspectRatio(for: parent.player) } } } static func dismantleUIViewController(_ uiViewController: UIViewController, coordinator: Coordinator) { parent.player.removeObserver(coordinator, forKeyPath: "currentItem") } }
步骤3:在SwiftUI视图中约束尺寸
在展示视频的父视图里,用获取到的宽高比约束视频视图的尺寸,这样红色背景就只会包裹视频内容:
struct ContentView: View { let player1 = AVPlayer(url: URL(string: "你的视频URL")!) @State private var videoAspectRatio: CGFloat? var body: some View { VideoPlayerUIView(player: player1, aspectRatio: $videoAspectRatio) .background(.red) .aspectRatio(videoAspectRatio, contentMode: .fit) } }
关键说明
- 取消了
playerLayer.frame = UIScreen.main.bounds的强制全屏设置,让视图尺寸由视频比例决定 - 通过
@Binding传递宽高比,配合.aspectRatio修饰符约束视图大小,消除多余的红色填充 - 添加观察者监听视频资源切换,确保宽高比能实时更新
内容的提问来源于stack exchange,提问作者RinaredTap
相关产品推荐
相关产品推荐

