SwiftUI:向UIViewRepresentable传参数并实现视频循环与控件隐藏
视频循环播放 + URL参数传递 + 隐藏控件解决方案
针对你的问题,我整理了两种可行的实现方案,直接解决URL参数传递、循环播放以及和隐藏控件结合的需求:
方案一:自定义UIView实现轻量播放
1. 修复URL传递逻辑
先给PlayerUIView添加支持URL参数的初始化方法,让它能接收从PlayerView传来的地址:
class PlayerUIView: UIView { private let playerLayer = AVPlayerLayer() private var player: AVPlayer? required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // 新增带URL参数的初始化方法 init(frame: CGRect, url: URL) { super.init(frame: frame) setupPlayer(with: url) } private func setupPlayer(with url: URL) { // 初始化播放器 player = AVPlayer(url: url) playerLayer.player = player playerLayer.videoGravity = .resizeAspectFill layer.addSublayer(playerLayer) // 配置循环播放 setupLooping() // 启动播放 player?.play() } private func setupLooping() { guard let player = player else { return } // 设置播放结束后不自动暂停 player.actionAtItemEnd = .none // 监听播放结束通知,触发循环 NotificationCenter.default.addObserver( self, selector: #selector(playerItemDidReachEnd), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem ) } @objc func playerItemDidReachEnd(notification: Notification) { // 回到视频开头并重新播放 player?.seek(to: .zero) player?.play() } override func layoutSubviews() { super.layoutSubviews() playerLayer.frame = bounds } // 销毁时移除通知监听,防止内存泄漏 deinit { NotificationCenter.default.removeObserver(self) } }
2. 修改PlayerView传递URL
让PlayerView在创建PlayerUIView时把URL传进去:
struct PlayerView: UIViewRepresentable { var url: URL func updateUIView(_ uiView: PlayerUIView, context: Context) { // 如果后续需要更新URL,可以在这里处理逻辑 } func makeUIView(context: Context) -> PlayerUIView { return PlayerUIView(frame: .zero, url: url) } }
3. 简化FinalPreview
原来的手动操作player逻辑可以删掉,现在PlayerUIView会自己管理播放:
struct FinalPreview: View { var url: URL let size: CGSize // 假设size是你已定义的尺寸变量 var body: some View { PlayerView(url: url) .aspectRatio(contentMode: .fill) .frame(width: size.width, height: size.height) .clipShape(RoundedRectangle(cornerRadius: 30, style: .continuous)) } }
方案二:结合AVPlayerViewController实现隐藏控件+循环
如果你想用已经实现了隐藏控件的AVPlayerViewController,只需要给它的播放器加上循环逻辑即可:
struct LoopingAVPlayerController: UIViewControllerRepresentable { var url: URL func makeUIViewController(context: Context) -> AVPlayerViewController { let player = AVPlayer(url: url) setupLooping(for: player) let controller = AVPlayerViewController() controller.player = player controller.showsPlaybackControls = false // 隐藏播放控件 return controller } func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) { // 处理URL更新的情况 if let currentItem = uiViewController.player?.currentItem, currentItem.url != url { let newPlayer = AVPlayer(url: url) setupLooping(for: newPlayer) uiViewController.player = newPlayer } } private func setupLooping(for player: AVPlayer) { player.actionAtItemEnd = .none NotificationCenter.default.addObserver( forName: .AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: .main ) { [weak player] _ in player?.seek(to: .zero) player?.play() } } // 清理通知,避免内存泄漏 static func dismantleUIViewController(_ uiViewController: AVPlayerViewController, coordinator: ()) { NotificationCenter.default.removeObserver( uiViewController, name: .AVPlayerItemDidPlayToEndTime, object: uiViewController.player?.currentItem ) } }
在FinalPreview中使用
直接替换成这个封装好的控制器即可:
struct FinalPreview: View { var url: URL let size: CGSize var body: some View { LoopingAVPlayerController(url: url) .aspectRatio(contentMode: .fill) .frame(width: size.width, height: size.height) .clipShape(RoundedRectangle(cornerRadius: 30, style: .continuous)) } }
关键注意事项
- 必须在销毁时移除通知监听,否则会导致内存泄漏
- 设置
actionAtItemEnd = .none只是让播放结束后不暂停,要实现循环必须手动调用seek(to: .zero)和play() - 两种方案任选:自定义UIView更轻量,AVPlayerViewController自带音频会话管理等底层处理
内容的提问来源于stack exchange,提问作者user4671973
相关产品推荐
相关产品推荐

