Implement a timer for an activity.

In my app, the user can set a timer for an activity. I want to show a simple progress bar that progresses every 1 second and stops (100%) at the end of the time interval of the timer.

example, think of the progressbar of a song being played which finishes on the length of the song.

I've read about TimerPublisher but am not sure how to implement it. Can someone help me out over here please?

Thanks Neerav

Accepted Reply

I implemented the timer this way... Only thing not working is invalidating the timer inspite of doing it on the main thread (same thread as firing the timer)

 @IBAction func playOrPauseButtonPressed(_ sender: Any) {
        var timer: Timer? = nil
        timerInterval = habit?.timer
        
        if !play!
        {
            if (timer == nil)
            {
                timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
                    
                    if self.secondsElapsed <= Int(self.timerInterval!)
                    {
                        self.secondsElapsed = self.secondsElapsed + 1
                        
                        let progress = Float(self.secondsElapsed)/Float(self.timerInterval!)
                        
                        DispatchQueue.main.async {
                            self.timerProgressView.setProgress(progress, animated: true)
                        }
                    }
                    else
                    {
                        DispatchQueue.main.async {
                            timer.invalidate()
                            self.playOrPauseButton.imageView?.image = UIImage(named: "Play")
                        }
                        self.play = false
                        self.secondsElapsed = 0
                    }
                }
            }
            DispatchQueue.main.async {
                self.playOrPauseButton.imageView?.image = UIImage(named: "Pause")
                timer?.fire()
            }
            play = true

        }
        else
        {
            DispatchQueue.main.async {
                self.playOrPauseButton.imageView?.image = UIImage(named: "Play")
                timer?.invalidate()
            }
            play = false
        }
    }

Any help with that second part of invalidating the thread? I see the timer keeps firing and the progress view keeps updating.

Replies

I think you want something like this: https://developer.apple.com/documentation/swiftui/progressview/init(timerinterval:countsdown:)

  • I'm using Storyboards and not SwiftUI

Add a Comment

Deleted...

I implemented the timer this way... Only thing not working is invalidating the timer inspite of doing it on the main thread (same thread as firing the timer)

 @IBAction func playOrPauseButtonPressed(_ sender: Any) {
        var timer: Timer? = nil
        timerInterval = habit?.timer
        
        if !play!
        {
            if (timer == nil)
            {
                timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
                    
                    if self.secondsElapsed <= Int(self.timerInterval!)
                    {
                        self.secondsElapsed = self.secondsElapsed + 1
                        
                        let progress = Float(self.secondsElapsed)/Float(self.timerInterval!)
                        
                        DispatchQueue.main.async {
                            self.timerProgressView.setProgress(progress, animated: true)
                        }
                    }
                    else
                    {
                        DispatchQueue.main.async {
                            timer.invalidate()
                            self.playOrPauseButton.imageView?.image = UIImage(named: "Play")
                        }
                        self.play = false
                        self.secondsElapsed = 0
                    }
                }
            }
            DispatchQueue.main.async {
                self.playOrPauseButton.imageView?.image = UIImage(named: "Pause")
                timer?.fire()
            }
            play = true

        }
        else
        {
            DispatchQueue.main.async {
                self.playOrPauseButton.imageView?.image = UIImage(named: "Play")
                timer?.invalidate()
            }
            play = false
        }
    }

Any help with that second part of invalidating the thread? I see the timer keeps firing and the progress view keeps updating.

Unfortunately I cannot undo the above post marked as answered...

Here is the correct solution with play, pause and stop...

    @IBAction func playOrPauseButtonPressed(_ sender: Any) {
        timerInterval = habit?.timer
        if !play!
        {
            if (timer == nil)
            {
                //make new timer
                timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
                    
                    //let timer be fired until time interval
                    if timer.isValid
                    {
                        self.secondsElapsed = self.secondsElapsed + 1
                        
                        let progress = Float(self.secondsElapsed)/Float(self.timerInterval!)
                        DispatchQueue.main.async() {
                            self.timerProgressView.setProgress(progress, animated: true)
                        }
                    }
                    else
                    {
                        DispatchQueue.main.async() {
                            self.playOrPauseButton.imageView?.image = UIImage(named: "Play")
                        }
                        self.play = false
                    }
                }
            }
            DispatchQueue.main.async()
            {
                self.timer?.fire()

                //when timer fires show pause button
                self.playOrPauseButton.imageView?.image = UIImage(named: "Pause")
            }

            play = true
        }
        else
        {
            DispatchQueue.main.async()
            {
                self.timer?.invalidate()
                self.timer = nil
                self.playOrPauseButton.imageView?.image = UIImage(named: "Play")
            }
            play = false
        }
    }
    
    @IBAction func stopButtonPressed(_ sender: Any) {
        DispatchQueue.main.async()
        {
            self.timer?.invalidate()
            self.timer = nil
            self.timerProgressView.setProgress(0, animated: true)
            self.playOrPauseButton.imageView?.image = UIImage(named: "Play")
        }
        play = false
        secondsElapsed = 0
    }
    ```

If i set the timer as nil outside the dispatch block, things dont work. dunno why.