Nouf
Nouf

Reputation: 773

Pausing and Resuming Timer in Swift

I am playing an audio which can be pausing, resuming and stoped I have also a slider which can be use to change the play the audio current point and a label that show duration of the audio and I want to pause the timer when the user pause the audio and I read If I want that I can invalidate and nill the timer then start it again , but the issue with that it will replay the audio from the start, is there is way to start the timer at the last point it was paused?

func startTimer() {
        if replayTimer == nil {
            replayTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateSlider), userInfo: nil, repeats: true)
        }
    }

  @objc func updateSlider() {
        progressBar.value = Float(audio.audio!.currentTime)
    }



 @IBAction func playReceiverVoicenote(_ sender: Any) {
        if  replayTimer == nil {
            audioBtn.setImage(#imageLiteral(resourceName: "pause"), for: .normal)
            audio.playAudio(filePath: filePath!)
            startTimer()
            receiverProgressBar.maximumValue = audio.getAudioDuration()
        } else if audio.isAudioPlaying() {
            audioBtn.setImage(#imageLiteral(resourceName: "playAudio"), for: .normal)
            audio.pauseAudio()
            replayTimer?.invalidate()
            replayTimer = nil
        } else {
            audioBtn.setImage(#imageLiteral(resourceName: "pause"), for: .normal)
            audio.replayAudio()
            startTimer()
        }
    }


   func playAudio(filePath:URL){
        do {
            audio = try AVAudioPlayer(contentsOf: filePath)
            audio!.delegate  = self
            audio!.prepareToPlay()
            audio!.volume = 1.0
            audio!.play()
        } catch {
            print(error.localizedDescription)
        }
    }

    func pauseAudio() {
        audio!.pause()
    }

    func replayAudio() {
        audio!.play()
    }

    func stopAudio() {
        audio!.stop()
    }     

Upvotes: 0

Views: 1846

Answers (2)

Mickael Belhassen
Mickael Belhassen

Reputation: 3342

You can also use this Audio Manager in this way:

let player = AudioPlayer()
player.loadAudio(url: URL(string: "myAudioUrl.mp3")!, name: "", img: "")

And the action of the button to play and pause:

if player.isPlaying {
    player.pauseAudio()
} else {
    player.playAudio { isFinish, player, currentTimeInSec, remainingTimeInSec in

        if isFinish {
            // Audio finish to play
        }
    }
}

Who return in block closure:

  • isFinish: Bool // If the audio has finished playing
  • player: AVAudioPlayer // The player
  • currentTimeInSec: Int // The current time in seconds of the audio playing
  • remainingTimeInsec: Int // The remaining time

AudioPlayer:

import Foundation
import AVFoundation
import MediaPlayer

class AudioPlayer {
    var audioPlayer: AVAudioPlayer?
    var hasBeenPaused = false
    var songName = ""
    var songImage = ""
    var timer: Timer?

    var isPlaying: Bool {
        return audioPlayer?.isPlaying ?? false
    }

    public func loadAudio(url: URL, name: String, img: String) {
        songName = name
        songImage = img

        setupRemoteTransportControls()

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: url)
            audioPlayer?.prepareToPlay()

            let audioSession = AVAudioSession.sharedInstance()

            do {
                try audioSession.setCategory(AVAudioSession.Category.playback, mode: .default)
            } catch let sessionError {
                print(sessionError)
            }
        } catch let songPlayerError {
            print(songPlayerError)
        }
    }

    public func playAudio(completion: ((_ isFinish: Bool, _ player: AVAudioPlayer, _ currentTimeInSec: Int, _ restTimeInSec: Int) -> ())? = nil) {
        guard let audioPlayer = audioPlayer else  { return }

        audioPlayer.play()
        setupNowPlaying()

        if timer == nil {
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
                let currentTime = Int(audioPlayer.currentTime)
                let remainingTime = Int(audioPlayer.duration) - Int(currentTime)

                if remainingTime == 0 {
                    completion?(true, audioPlayer, currentTime, remainingTime)
                    if self.timer != nil {
                        self.timer!.invalidate()
                        self.timer = nil
                    }
                } else {
                    completion?(false, audioPlayer, currentTime, remainingTime)
                }
            }
        }
    }

    public func pauseAudio() {
        guard let audioPlayer = audioPlayer else  { return }

        if audioPlayer.isPlaying {
            audioPlayer.pause()
            hasBeenPaused = true
        } else {
            hasBeenPaused = false
        }

        setupNowPlaying()

        if timer != nil {
            timer!.invalidate()
            timer = nil
        }
    }

    public func replayAudio() {
        guard let audioPlayer = audioPlayer else  { return }

        if audioPlayer.isPlaying || hasBeenPaused {
            audioPlayer.stop()
            audioPlayer.currentTime = 0
            audioPlayer.play()
        } else {
            audioPlayer.play()
        }

        setupNowPlaying()
    }

    public func stopAudio() {
        guard let audioPlayer = audioPlayer else  { return }

        audioPlayer.stop()
        setupNowPlaying()

        if timer != nil {
            timer!.invalidate()
            timer = nil
        }
    }

    func setupRemoteTransportControls() {
        let commandCenter = MPRemoteCommandCenter.shared()

        commandCenter.previousTrackCommand.isEnabled = false
        commandCenter.nextTrackCommand.isEnabled = false
        commandCenter.skipBackwardCommand.isEnabled = false
        commandCenter.skipForwardCommand.isEnabled = false

        commandCenter.playCommand.isEnabled = true
        commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            //Update your button here for the play command
            self.playAudio()
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updatedControlCenterAudio"), object: nil)
            return .success
        }

        commandCenter.pauseCommand.isEnabled = true
        commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            //Update your button here for the pause command
            self.pauseAudio()
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updatedControlCenterAudio"), object: nil)
            return .success
        }
    }

    func setupNowPlaying() {
        guard let audioPlayer = audioPlayer else  { return }

        var nowPlayingInfo = [String: Any]()
        nowPlayingInfo[MPMediaItemPropertyTitle] = songName

        if let image = UIImage(named: songImage) {
            nowPlayingInfo[MPMediaItemPropertyArtwork] =
                    MPMediaItemArtwork(boundsSize: image.size) { size in
                        return image
                    }
        }

        nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = audioPlayer.currentTime
        nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = audioPlayer.duration
        nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = audioPlayer.rate

        // Set the metadata
        MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
    }

}

Upvotes: 0

vadian
vadian

Reputation: 285069

  • When the audio is going to pause save audio.currentTime in a variable and invalidate the timer.
  • When the audio is going to resume get the saved currentTime, call play(atTime:) passing the time interval and restart the timer

Upvotes: 2

Related Questions