SwiftUI VideoPlayer获取当前播放时间、隐藏控件及控制播放的实现方法
问题分析
为什么onChange回调无法触发打印?
onChange修饰符仅能监听由SwiftUI响应式属性包装器(如@State、@ObservedObject)托管的变量变化。AVPlayer.currentTime()是普通的同步方法调用,AVPlayer的播放进度更新不会主动通知SwiftUI视图刷新,因此onChange不会被触发。
完整实现方案
以下代码可直接在MacOS Playground运行,完全满足你的三个需求:
- 支持本地/远程URL初始化播放器
- 支持指定时间戳自动暂停播放
- 完全隐藏原生播放器控件
import SwiftUI import PlaygroundSupport import AVFoundation // 播放器ViewModel,管理播放逻辑 class VideoPlayerViewModel: ObservableObject { let player: AVPlayer private var timeObserver: Any? // 初始化支持传入本地/远程URL init(url: URL) { let playerItem = AVPlayerItem(url: url) self.player = AVPlayer(playerItem: playerItem) addTimeObserver() } // 添加进度监听,间隔0.1秒回调一次 private func addTimeObserver() { let interval = CMTimeMakeWithSeconds(0.1, preferredTimescale: 600) timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] currentTime in guard let self = self else { return } let currentSeconds = CMTimeGetSeconds(currentTime) guard !currentSeconds.isNaN else { return } print("当前播放时间:\(currentSeconds)秒") // 示例:播放到10秒时自动暂停,可按需修改为60秒等自定义阈值 if currentSeconds >= 10 { self.player.pause() // 暂停后可移除监听避免重复触发 if let observer = self.timeObserver { self.player.removeTimeObserver(observer) self.timeObserver = nil } } } } deinit { if let observer = timeObserver { player.removeTimeObserver(observer) } } } // 自定义无控件播放器视图(MacOS适配) struct CustomVideoPlayer: NSViewRepresentable { let player: AVPlayer func makeNSView(context: Context) -> NSView { let view = NSView(frame: .zero) let playerLayer = AVPlayerLayer(player: player) playerLayer.videoGravity = .resizeAspect playerLayer.frame = view.bounds view.layer = playerLayer view.wantsLayer = true return view } func updateNSView(_ nsView: NSView, context: Context) { guard let layer = nsView.layer as? AVPlayerLayer else { return } layer.frame = nsView.bounds } } // 业务视图 struct VideoView: View { @StateObject private var viewModel: VideoPlayerViewModel // 初始化传入视频URL init(url: URL) { _viewModel = StateObject(wrappedValue: VideoPlayerViewModel(url: url)) } var body: some View { CustomVideoPlayer(player: viewModel.player) .frame(width: 800, height: 450) .onAppear { viewModel.player.play() } .onDisappear { viewModel.player.pause() } } } // 测试运行 let testUrl = URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")! PlaygroundPage.current.setLiveView(VideoView(url: testUrl))
关键逻辑说明
- 隐藏原生控件:通过封装
AVPlayerLayer直接渲染视频,完全绕过了AVKit自带的VideoPlayer组件的控件渲染逻辑,不会出现控件短暂显示的问题。 - 进度监听:使用AVPlayer官方提供的周期性时间观察者实现进度获取,性能开销低,回调时间精度可自定义。
- 时间戳控制:在进度回调中判断当前播放时间,达到阈值后调用
pause()即可实现自动暂停,也可扩展支持跳转到指定时间戳播放、多时间点触发自定义逻辑等需求。
内容的提问来源于stack exchange,提问作者Aiyush
相关产品推荐
相关产品推荐

