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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 15:54:56