SwiftUI中如何为AVPlayer视频循环添加平滑过渡效果?
给循环视频播放器添加循环间平滑过渡效果
嘿,我来帮你搞定这个循环过渡的需求!你的现有代码用了AVPlayerLooper实现自动循环,但它没法直接插入过渡动画,所以我们需要调整一下播放逻辑,手动控制循环,这样就能在每次循环之间加入你想要的模糊(或者其他)过渡效果了。
核心思路
- 移除自动循环的
AVPlayerLooper,改为手动监听视频结束事件,触发过渡动画后再重新启动播放 - 添加一个过渡视图(比如模糊效果),在循环间隙显示
- 通过动画控制过渡视图的显示/隐藏,实现平滑衔接
修改后的完整代码
import SwiftUI import AVFoundation // MARK: ExerciseVideoPlayer: UIViewRepresentable struct ExerciseVideoPlayer: UIViewRepresentable { var increaseRepetitions: () -> Void var videoUrl: URL func makeUIView(context: Context) -> UIView { LoopingVideoPlayerView(repetitions: increaseRepetitions, videoUrl: videoUrl) } func updateUIView(_ uiView: UIView, context: Context) { // Do nothing here } } // MARK: LoopingVideoPlayerView: UIView class LoopingVideoPlayerView: UIView { // MARK: Stored properties var increaseRepetitions: () -> Void var videoUrl: URL private var playerLayer = AVPlayerLayer() private var player: AVQueuePlayer! private var blurView: UIVisualEffectView! // 过渡用的模糊视图 // MARK: Initialization init(repetitions: @escaping () -> Void, videoUrl: URL) { self.increaseRepetitions = repetitions self.videoUrl = videoUrl super.init(frame: .zero) // 1. 初始化模糊过渡视图 let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialDark) blurView = UIVisualEffectView(effect: blurEffect) blurView.frame = bounds blurView.alpha = 0 // 默认隐藏 addSubview(blurView) // 2. 初始化播放器 let playerItem = AVPlayerItem(url: videoUrl) player = AVQueuePlayer(playerItem: playerItem) playerLayer.player = player playerLayer.videoGravity = .resizeAspect layer.addSublayer(playerLayer) // 3. 监听视频播放结束事件 NotificationCenter.default.addObserver( self, selector: #selector(handleVideoEnd), name: .AVPlayerItemDidPlayToEndTime, object: playerItem ) // 开始播放 player.play() } // MARK: Methods override func layoutSubviews() { super.layoutSubviews() playerLayer.frame = bounds blurView.frame = bounds // 确保模糊视图铺满整个播放器 } // 处理视频结束逻辑:触发过渡动画,然后重新播放 @objc private func handleVideoEnd() { // 先调用计数增加 increaseRepetitions() // 播放结束,显示模糊过渡动画 UIView.animate(withDuration: 0.3) { self.blurView.alpha = 1 } completion: { _ in // 延迟一会儿(比如0.2秒),然后隐藏模糊并重新播放 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { UIView.animate(withDuration: 0.3) { self.blurView.alpha = 0 } completion: { _ in // 重新加载视频项并播放 let newPlayerItem = AVPlayerItem(url: self.videoUrl) self.player.replaceCurrentItem(with: newPlayerItem) // 重新监听新的播放项结束事件 NotificationCenter.default.addObserver( self, selector: #selector(self.handleVideoEnd), name: .AVPlayerItemDidPlayToEndTime, object: newPlayerItem ) self.player.play() } } } } // MARK: 禁用coder初始化 @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // MARK: ExerciseVideoPlayer_Previews: PreviewProvider struct ExerciseVideoPlayer_Previews: PreviewProvider { static var previews: some View { guard let videoUrl = URL(string: "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4") else { fatalError("videoURL not found") } return ExerciseVideoPlayer(increaseRepetitions: {}, videoUrl: videoUrl) } }
关键改动说明
- 移除
AVPlayerLooper:自动循环会跳过我们想要插入的过渡环节,改为手动控制每次循环的结束和重启,给过渡效果留出空间 - 添加模糊过渡视图:用
UIVisualEffectView实现系统原生模糊效果,你可以根据需求换成其他样式(比如浅色模糊、自定义毛玻璃效果) - 修改结束事件处理:视频播放结束后,先执行计数增加逻辑,再触发模糊淡入动画;延迟片刻后淡出模糊,重新加载视频项并启动播放,同时绑定新播放项的结束监听
- 动态布局适配:确保模糊视图在播放器尺寸变化时能铺满整个区域,避免出现错位
自定义过渡效果
如果你想换其他过渡,比如淡入淡出黑色背景,只需要把模糊视图换成普通UIView,修改动画逻辑即可:
// 替换模糊视图为黑色背景 let fadeView = UIView() fadeView.backgroundColor = .black fadeView.alpha = 0 addSubview(fadeView)
然后调整动画中的透明度变化参数,就能实现不同的过渡风格,非常灵活。
内容的提问来源于stack exchange,提问作者Arno Claude
相关产品推荐
相关产品推荐

