/// https://stackoverflow.com/questions/58779184/how-to-control-avplayer-in-swiftui import SwiftUI import AVKit import Combine struct TestView: View { private var player = MediaPlayer(url: Bundle.main.url(forResource: "FileName", withExtension: "mp4")!) var body: some View { VStack { VideoPlayer(player: player.player) SliderView(player: player) } } } struct TestView_Previews: PreviewProvider { static var previews: some View { TestView() } } class MediaPlayer { var player: AVPlayer var currentTimePublisher: PassthroughSubject = .init() var currentProgressPublisher: PassthroughSubject = .init() private var playerPeriodicObserver: Any? init(url: URL) { player = AVPlayer(url: url) setupPeriodicObservation(for: player) } private func setupPeriodicObservation(for player: AVPlayer) { let timeScale = CMTimeScale(NSEC_PER_SEC) let time = CMTime(seconds: 0.5, preferredTimescale: timeScale) playerPeriodicObserver = player.addPeriodicTimeObserver(forInterval: time, queue: .main) { [weak self] (time) in guard let `self` = self else { return } let progress = self.calculateProgress(currentTime: time.seconds) self.currentProgressPublisher.send(progress) self.currentTimePublisher.send(time.seconds) } } private func calculateProgress(currentTime: Double) -> Float { return Float(currentTime / duration) } private var duration: Double { return player.currentItem?.duration.seconds ?? 0 } func play() { player.play() } func pause() { player.pause() } func seek(to time: CMTime) { player.seek(to: time) } func seek(to percentage: Float) { let time = convertFloatToCMTime(percentage) player.seek(to: time) } private func convertFloatToCMTime(_ percentage: Float) -> CMTime { return CMTime(seconds: duration * Double(percentage), preferredTimescale: CMTimeScale(NSEC_PER_SEC)) } } class PlayerSliderViewModel: ObservableObject { @Published var progressValue: Float = 0 var player: MediaPlayer var acceptProgressUpdates = true var subscriptions: Set = .init() init(player: MediaPlayer) { self.player = player listenToProgress() } private func listenToProgress() { player.currentProgressPublisher.sink { [weak self] progress in guard let self = self, self.acceptProgressUpdates else { return } self.progressValue = progress }.store(in: &subscriptions) } func didSliderChanged(_ didChange: Bool) { acceptProgressUpdates = !didChange if didChange { player.pause() } else { player.seek(to: progressValue) player.play() } } } struct SliderView: View { @ObservedObject var viewModel: PlayerSliderViewModel init(player: MediaPlayer) { viewModel = .init(player: player) } var body: some View { Slider(value: $viewModel.progressValue) { didChange in viewModel.didSliderChanged(didChange) } } }