Reputation: 15
The goal is: to count how many days have passed since the user entered the number of desired days, e.g. user enters 3 days and the counting begins (1, 2, 3 -> stop).
(I changed days to seconds to check the logic.)
The problem: I've tried several methods but I always come to the same problem: timer stops at entered number of days - 1 (e.g.: 3 was entered and 2 is the last number on the screen, but must be 3!)
Where am I wrong?
My code:
import UIKit
class ViewController: UIViewController {
private let mainLabel = UILabel.makeLabel(text: "Day number")
private let startButton = UIButton.makeButton("Start", titleColor: .red, bgColor: .lightGray)
private let stopButton = UIButton.makeButton("Stop", titleColor: .black, bgColor: .lightGray)
private var startDate: Date?
private var endDate: Date?
private let startTimeKey = "StartTime"
private let endTimeKey = "EndDate"
private weak var timer: Timer?
private let defaults = UserDefaults.standard
let dateComponentsFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
setupLayouts()
endDate = defaults.value(forKey: endTimeKey) as? Date
startDate = defaults.value(forKey: startTimeKey) as? Date
if let endTime = endDate, let startTime = startDate {
if endTime < Date() {
startTimer(endTime: endTime, startTime: startTime)
} else {
timer?.invalidate()
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startButton.addTarget(self, action: #selector(startButtonIsPressed), for: .touchUpInside)
stopButton.addTarget(self, action: #selector(stopButtonIsPressed), for: .touchUpInside)
}
@objc func startButtonIsPressed() {
let alert = UIAlertController(title: "Hello", message: "Enter number of days", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default) { [unowned alert] _ in
guard let daysNumber = alert.textFields?.first?.text, !daysNumber.isEmpty else { return }
self.startDate = Date()
self.defaults.set(self.startDate, forKey: self.startTimeKey)
self.endDate = Calendar.current.date(byAdding: .second, value: Int(daysNumber)!, to: Date())
self.defaults.set(self.endDate, forKey: "EndDate")
if self.endDate! > self.startDate! {
self.timer = nil
self.startTimer(endTime: self.endDate!, startTime: self.startDate!)
}
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(ok)
alert.addAction(cancel)
alert.addTextField { (textField) in
textField.placeholder = "Number"
textField.keyboardType = .numberPad
}
present(alert, animated: true)
}
@objc func stopButtonIsPressed() {
timer?.invalidate()
}
func startTimer(endTime: Date, startTime: Date) {
defaults.set(endTime, forKey: endTimeKey)
defaults.set(startTime, forKey: startTimeKey)
self.endDate = endTime
self.startDate = startTime
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(handleTimer), userInfo: nil, repeats: true)
}
@objc func handleTimer() {
if endDate! > Date() {
mainLabel.text = dateComponentsFormatter.string(from: startDate!, to: Date())
} else {
timer?.invalidate()
}
}
func setupLayouts() {
view.addSubview(mainLabel)
view.addSubview(startButton)
view.addSubview(stopButton)
NSLayoutConstraint.activate([
mainLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
mainLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
startButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -70),
startButton.topAnchor.constraint(equalTo: mainLabel.bottomAnchor, constant: 140),
stopButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 70),
stopButton.topAnchor.constraint(equalTo: mainLabel.bottomAnchor, constant: 140),
])
}
}
Upvotes: 0
Views: 69