Best way to implement a smooth, continuous UISlider to seek Audio [Swift]

I am using AVAssets to implement an Audio Player, but have been running into issues with the implementation of a UISlider for seeking audio and showing audio progress. I had initially been using

seeker?.value = currentValue

As a way to "animate" the UISlider to show the progress in the Audio. However, this animation was very chunky and did not look smooth. So instead, I went with an animation approach where I animated the UISlider to progress from it's start value (0) to it's end value (1) using a UIViewPropertyAnimator with animation duration of the Audio file. However, this has been causing me some other troubles including a difficulty with scrubbing through the audio once the animation begins.

I was wondering if anyone has any ideas on how to do this sort of thing that works so flawlessly on most popular streaming app. Thanks!

1 answer

  • answered 2020-07-05 04:05 Tomo Norbert

    For a quick and good enough solution, we have used this approach in one of our project.

    Have some class properties:

    private var player: AVPlayer? = nil
    private var currentTimer: Timer!
    

    In your viewdidload, call the setCurrentTimer method

    override func viewDidLoad() {
        super.viewDidLoad()
        
        setCurrentTimer()
        .
        .
        .
        setupUI()
    }
    

    Your setCurrentTimer should look like this:

    private func setCurrentTimer() {
        currentTimer?.invalidate()
        currentTimer = nil
        
        currentTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { [weak self](timer) in
            guard let self = self else {
                timer.invalidate()
                return
            }
            
    
            guard let player = self.player, let currentItem = player.currentItem else {
                return
            }
            
            //Set your slider value here
            player.currentItem?.currentTime()
        })
    }
    

    Basically, at a frequent rate, you request the current time from the player and after some math, you can set a new value for your slider (actual animation is not needed because it is updated almost instantly and the user sees a smooth slider this way). It is very smooth, if you are using a UISlider read this post about how to detect if the user is scrubbing. While the user is scrubbing, you can set a bool to true and you can check against this value in your timer so the slider will not jump all around.

    Since the timer block gets a weak self and if the self is nil, it invalidates itself, you don't have to worry about bad access or crashes but you can remove your time in your class deinit anyway, just to be sure.

    func deinit() {
        currentTimer?.invalidate()
        currentTimer = nil
    }