You need to enable JavaScript to run this app. SwiftUI VideoPlayer获取当前播放时间、隐藏控件及控制播放的实现方法
优惠活动
大模型
产品
解决方案
定价
更多

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))

关键逻辑说明

  1. 隐藏原生控件:通过封装AVPlayerLayer直接渲染视频,完全绕过了AVKit自带的VideoPlayer组件的控件渲染逻辑,不会出现控件短暂显示的问题。
  2. 进度监听:使用AVPlayer官方提供的周期性时间观察者实现进度获取,性能开销低,回调时间精度可自定义。
  3. 时间戳控制:在进度回调中判断当前播放时间,达到阈值后调用pause()即可实现自动暂停,也可扩展支持跳转到指定时间戳播放、多时间点触发自定义逻辑等需求。

内容的提问来源于stack exchange,提问作者Aiyush

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 08:06:08