Search code examples
swifttimerlabelresumepause

display timer that was paused and resumed on a label that displays a paused timer Int


My swift code below should has 2 buttons a start and pause button. Right now my code takes a int on a textfield and counts it down and display on label lblTime. When the pause button is tapped the label timer stops. When it is hit again the timer goes down by 1 seconds and it should count down just like it did when the start button is tapped. With the timer coutning down from what is on the label. Instead of counting down by just one second I want it to count down to zero. Look at func pause.

import UIKit

   class ViewController: UIViewController, UITextFieldDelegate {
let MAX_LENGTH_PHONENUMBER = 2
let ACCEPTABLE_NUMBERS     = "0123456789"
var enterTime = UITextField()
var lblTime = UILabel()
var startBTN = UIButton()
var timer = Timer()
var counter = 0

var pauseBTN = UIButton()
var jake = 0


override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    [enterTime,lblTime,startBTN,pauseBTN].forEach{
        $0.backgroundColor = .systemRed
        view.addSubview($0)
        $0.translatesAutoresizingMaskIntoConstraints = false
    }

    enterTime.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: 60, height: 50)
    lblTime.frame = CGRect(x: view.center.x-115, y: view.center.y, width: 60, height: 50)
    pauseBTN.frame = CGRect(x: view.center.x-115, y: view.center.y+90, width: 60, height: 50)
    startBTN.frame = CGRect(x: view.center.x-115, y: view.center.y+200, width: 60, height: 50)

    startBTN.addTarget(self, action: #selector(startHit), for: .touchDown)

    pauseBTN.addTarget(self, action: #selector(pause), for: .touchDown)

    enterTime.placeholder = String("MM:SS")

    enterTime.delegate = self
    enterTime.addTarget(self, action: #selector(self.textFieldDidChange), for: UIControl.Event.editingChanged)

    pauseBTN.setTitle("pause", for: .normal)
    startBTN.setTitle("Start", for: .normal)
}

@objc func pause() {


    if jake == 0 {
        timer.invalidate()
        pauseBTN.setTitle("go", for: .normal)


    }
    else {
        timer.fire()
        pauseBTN.setTitle("pause", for: .normal)
        counter -= 1
        lblTime.text = formatSeconds(counter)
        if ( counter == 0 ) {
            timer.invalidate()}
    }

    jake += 1

}

@objc func startHit() {
    timer.invalidate()
    counter = convertToSeconds(from: enterTime.text!)
    lblTime.text = formatSeconds(counter)
    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}

@objc func timerAction() {
    counter -= 1
    lblTime.text = formatSeconds(counter)
    if ( counter == 0 ) {
        timer.invalidate()
    }
}
func convertToSeconds(from timeString: String) -> Int {
    let components = timeString.components(separatedBy: ":")
    if components.count == 2 {
        let minutes = Int(components[0]) ?? 0
        let seconds = Int(components[1]) ?? 0
        return (minutes * 60) + seconds
    } else if components.count == 3 {
        let hours = Int(components[0]) ?? 0
        let minutes = Int(components[1]) ?? 0
        let seconds = Int(components[2]) ?? 0
        return (hours * 60 * 60) + (minutes * 60) + seconds
    } else {
        return 0
    }
}
func formatSeconds(_ totalSeconds: Int) -> String {
    let formatter = DateComponentsFormatter()
    formatter.unitsStyle = .positional
    formatter.allowedUnits = [.minute, .second]
    formatter.zeroFormattingBehavior = .pad
    return formatter.string(from: TimeInterval(totalSeconds))!
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let newLength: Int = textField.text!.count + string.count - range.length
    let numberOnly = NSCharacterSet.init(charactersIn: ACCEPTABLE_NUMBERS).inverted
    let strValid = string.rangeOfCharacter(from: numberOnly) == nil
    return (strValid && (newLength <= MAX_LENGTH_PHONENUMBER))
}



@objc func textFieldDidChange(_ textField: UITextField) {
    if  textField.text!.count == 2  {
        textField.text = textField.text! + ":00"
    }
}
 }

Solution

  • to pause/stop timer use timer.invalidate() and timer = nil both, because at starting of timer it is always making new instance.

    class ViewController: UIViewController, UITextFieldDelegate {
        let MAX_LENGTH_PHONENUMBER = 2
        let ACCEPTABLE_NUMBERS     = "0123456789"
        var enterTime = UITextField()
        var lblTime = UILabel()
        var startBTN = UIButton()
        var timer:Timer? = Timer()
        var counter = 0
    
        var pauseBTN = UIButton()
        var isPaused = false
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            [enterTime,lblTime,startBTN,pauseBTN].forEach{
                $0.backgroundColor = .systemRed
                view.addSubview($0)
                $0.translatesAutoresizingMaskIntoConstraints = false
            }
            enterTime.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: 60, height: 50)
            lblTime.frame = CGRect(x: view.center.x-115, y: view.center.y, width: 60, height: 50)
            pauseBTN.frame = CGRect(x: view.center.x-115, y: view.center.y+90, width: 60, height: 50)
            startBTN.frame = CGRect(x: view.center.x-115, y: view.center.y+200, width: 60, height: 50)
    
            startBTN.addTarget(self, action: #selector(startHit), for: .touchDown)
    
            pauseBTN.addTarget(self, action: #selector(pause), for: .touchDown)
    
            enterTime.placeholder = String("MM:SS")
    
            enterTime.delegate = self
            enterTime.addTarget(self, action: #selector(self.textFieldDidChange), for: UIControl.Event.editingChanged)
    
            pauseBTN.setTitle("pause", for: .normal)
            startBTN.setTitle("Start", for: .normal)
        }
    
        @objc func pause() {
    
            if !isPaused {
                timer?.invalidate()
                timer = nil
                pauseBTN.setTitle("go", for: .normal)
            }
            else {
                timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
                pauseBTN.setTitle("pause", for: .normal)
            }
            isPaused = !isPaused
        }
    
        @objc func startHit() {
            isPaused = false
            timer?.invalidate()
            counter = convertToSeconds(from: enterTime.text ?? "")
            lblTime.text = formatSeconds(counter)
            timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
        }
    
        @objc func timerAction() {
            if ( counter == 0 ) {
                timer?.invalidate()
                timer = nil
                return
            }
            counter -= 1
            lblTime.text = formatSeconds(counter)
        }
        func convertToSeconds(from timeString: String) -> Int {
            let components = timeString.components(separatedBy: ":")
            if components.count == 1 {
                let minutes = Int(components[0]) ?? 0
                return minutes * 60
            } else if components.count == 2 {
                let minutes = Int(components[0]) ?? 0
                let seconds = Int(components[1]) ?? 0
                return (minutes * 60) + seconds
            } else if components.count == 3 {
                let hours = Int(components[0]) ?? 0
                let minutes = Int(components[1]) ?? 0
                let seconds = Int(components[2]) ?? 0
                return (hours * 60 * 60) + (minutes * 60) + seconds
            } else {
                return 0
            }
        }
        func formatSeconds(_ totalSeconds: Int) -> String {
            let formatter = DateComponentsFormatter()
            formatter.unitsStyle = .positional
            formatter.allowedUnits = [.minute, .second]
            formatter.zeroFormattingBehavior = .pad
            return formatter.string(from: TimeInterval(totalSeconds)) ?? ""
        }
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
            let newLength: Int = (textField.text?.count ?? 0) + string.count - range.length
            let numberOnly = NSCharacterSet.init(charactersIn: ACCEPTABLE_NUMBERS).inverted
            let strValid = string.rangeOfCharacter(from: numberOnly) == nil
            return (strValid && (newLength <= MAX_LENGTH_PHONENUMBER))
        }
    
    
    
        @objc func textFieldDidChange(_ textField: UITextField) {
            if let text = textField.text, text.count == 2 {
                textField.text = text + ":00"
            }
        }
    }