You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中如何让AVPlayer/VideoPlayer内容填满可用空间?

SwiftUI中实现视频内容完全填满可用空间的解决方案

问题背景

开发SwiftUI应用时,需用AVPlayer展示视频,要求视频完全填满可用空间,无空白或意外裁切,但尝试两种方法后未达预期:

尝试1:用UIViewRepresentable封装AVPlayerViewController

代码如下:

import SwiftUI
import AVKit

struct VideoPlayerView: UIViewControllerRepresentable {
    let player: AVPlayer

    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let controller = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false
        controller.videoGravity = .resizeAspectFill
        return controller
    }

    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {}
}

设置videoGravity = .resizeAspectFill后,内容位置调整未达预期。

尝试2:使用VideoPlayer库

代码如下:

import SwiftUI
import VideoPlayer

struct CustomVideoPlayerView: View {
    @State private var player = AVPlayer(url: Bundle.main.url(forResource: "xxxx", withExtension: "mp4")!)

    var body: some View {
        VideoPlayer(player: player)
            .aspectRatio(contentMode: .fill) // 调整视频填满空间
            .frame(height: 300)
            .clipped() // 确保视频不超出可视区域
            .onAppear {
                player.play()
            }
            .onDisappear {
                player.pause()
                player.seek(to: .zero)
            }
    }
}

设置aspectRatio(contentMode: .fill)后,仍存在空白或裁切问题,无法完全覆盖指定区域。

需求

  • 实现SwiftUI中视频内容完全填满可用空间的方案
  • 达到类似AVPlayerLayer的.resizeAspectFill效果

解决方案

方案1:优化UIViewRepresentable封装

AVPlayerViewController自带默认布局边距,需手动覆盖约束确保填满父视图:

import SwiftUI
import AVKit

struct FullScreenVideoPlayer: UIViewControllerRepresentable {
    let player: AVPlayer
    
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let controller = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false
        controller.videoGravity = .resizeAspectFill
        // 移除默认布局约束,强制适配父视图
        controller.view.translatesAutoresizingMaskIntoConstraints = false
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
        guard let parentView = uiViewController.view.superview else { return }
        NSLayoutConstraint.activate([
            uiViewController.view.topAnchor.constraint(equalTo: parentView.topAnchor),
            uiViewController.view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor),
            uiViewController.view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor),
            uiViewController.view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor)
        ])
    }
}

使用示例:

struct ContentView: View {
    let player = AVPlayer(url: Bundle.main.url(forResource: "video", withExtension: "mp4")!)
    
    var body: some View {
        FullScreenVideoPlayer(player: player)
            .frame(height: 300)
            .clipped()
            .onAppear {
                player.play()
            }
    }
}

方案2:直接封装AVPlayerLayer(精准控制)

跳过AVPlayerViewController,直接封装AVPlayerLayer,避免控制器默认布局干扰:

import SwiftUI
import AVKit

struct AVPlayerLayerView: UIViewRepresentable {
    let player: AVPlayer
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.videoGravity = .resizeAspectFill
        playerLayer.frame = view.bounds
        playerLayer.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.layer.addSublayer(playerLayer)
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        guard let playerLayer = uiView.layer.sublayers?.first as? AVPlayerLayer else { return }
        playerLayer.player = player
        playerLayer.frame = uiView.bounds
    }
}

使用示例:

struct ContentView: View {
    let player = AVPlayer(url: Bundle.main.url(forResource: "video", withExtension: "mp4")!)
    
    var body: some View {
        AVPlayerLayerView(player: player)
            .frame(height: 300)
            .clipped()
            .onAppear {
                player.play()
            }
            .onDisappear {
                player.pause()
                player.seek(to: .zero)
            }
    }
}

方案3:优化iOS 16+原生VideoPlayer

iOS 16及以上版本的原生VideoPlayer支持更精准布局,配合scaledToFill和全屏约束实现效果:

import SwiftUI
import AVKit

struct FullScreenNativeVideoPlayer: View {
    @State private var player = AVPlayer(url: Bundle.main.url(forResource: "video", withExtension: "mp4")!)
    
    var body: some View {
        VideoPlayer(player: player)
            .scaledToFill()
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .clipped()
            .ignoresSafeArea() // 如需填充安全区域外空间可启用
            .onAppear {
                player.play()
            }
            .onDisappear {
                player.pause()
                player.seek(to: .zero)
            }
    }
}

关键说明

  • .resizeAspectFill的核心是保持视频比例,填满视图,裁切超出部分,必须配合clipped()确保超出部分不显示
  • AVPlayerViewController自带布局边距,直接封装时需手动覆盖约束
  • 直接使用AVPlayerLayer能获得最精准的布局控制,避免控制器的额外干扰

内容的提问来源于stack exchange,提问作者Renan Araújo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 17:43:24