How to loop NSTimer in SWIFT with a specific number of counting - swift

I am trying to loop a timer with a specific number of counting...
eg... 1, 2, 3, 4, 5
then loop again, the same counting, 10 times, then stop.
I was able to do the counting, but I can't do the looping of the counting.
What am I doing wrong?
var times = 0
var stopCounting = 10
#IBAction func buttonPressed(sender: UIButton) {
self.startTimer()
}
func startTimer(){
self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("startCounting"), userInfo: nil, repeats: true)
}
func startCounting (){
times += 1
if times < stopCounting + 1{
if counter > -1 && counter < 6 {
counting.text = String(counter++)
} else if counter == Int(counting.text) {
counting.text = "0"
}
}

import UIKit
class ViewController: UIViewController {
#IBOutlet weak var strTime: UILabel!
var timer = NSTimer()
var endTime: NSTimeInterval = 0
var now: NSTimeInterval { return NSDate().timeIntervalSinceReferenceDate }
var times = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func updateText(){
let time = Int(ceil(endTime - now))
if time == 0 {
times++
if times == 10 {
strTime.text = "end"
timer.invalidate()
}
endTime = NSDate().timeIntervalSinceReferenceDate + 5
} else {
strTime.text = time.description
}
}
#IBAction func buttonPressed(sender: UIButton) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "updateText", userInfo: nil, repeats: true)
endTime = NSDate().timeIntervalSinceReferenceDate + 5
sender.hidden = true
}
}
to make it count from 1 to 5 you need to change from ceil to floor, abs() and add +1
func updateText(){
let time = Int(floor(abs(endTime - now - 5)))
if time == 5 {
times++
if times == 10 {
strTime.text = "10" + " " + "0"
timer.invalidate()
}
endTime = NSDate().timeIntervalSinceReferenceDate + 5
} else {
strTime.text = times.description + " " + (time+1).description
}
}

Never done swift, but took a stab in the dark, why not.
var loops = 5 // Number of times to loop the counting
var countTo = 10 // Number to count to
var loopCount = 0 // Number of loops
#IBAction func buttonPressed(sender: UIButton) {
self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: Selector("startCounting"),
userInfo: nil,
repeats: true)
}
func startCounting (){
// Loop from 1 to countTo, setting the text for each number
for i in 1..countTo {
counting.text = String(i)
}
// Done counting, reset to zero
counting.text = "0"
// Update the number of times the counting has run
loopCount++
// If have completed all the loops, invalidate the timer
if (loopCounter >= loops) {
self.timer.invalidate()
}
}

Related

How Can I Make A Timer Using String In xCode

So I'm trying to make a chess timer. I'm using string to make the 00:00 in a variable called storeTimed. Is there a way possible to make that used an timer to count down from that?
Here's my code:
The part I need help with is the updateTimer() function. Since the storedTime is a string trying to pass as a Int, xCode isn't liking it. I'm honestly not very sure what I could do. Mostly at the else statement, but the whole part in general
class ChessTimer: UIViewController {
#IBOutlet weak var playerTimer1: UILabel!
#IBOutlet weak var playerTimer2: UILabel!
var timer = Timer()
var time = 10
var isTimerRunning = false
var storedTime = "00:00"
override func viewDidLoad() {
super.viewDidLoad()
if isTimerRunning == false {
runTimer()
}
}
#IBAction func restartButton(_ sender: UIButton) {
}
#IBAction func pausePressed(_ sender: UIButton) {
timer.invalidate()
}
#IBAction func settingsPressed(_ sender: UIButton) {
performSegue(withIdentifier: "goToSettings", sender: self)
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self,selector: (#selector(ChessTimer.updateTimer)),userInfo: nil, repeats: true)
isTimerRunning = true
}
#objc func updateTimer() {
if Int(storedTime) < 1 {
timer.invalidate()
playerTimer1.text = "00:00"
playerTimer2.text = "00:00"
}
else {
Int(storedTime)! -= 1
playerTimer1.text = prodTimeString(time: TimeInterval(storedTime)!)
}
}
func prodTimeString(time: TimeInterval) -> String {
let prodMinutes = Int(time) / 60 % 60
let prodSeconds = Int(time) % 60
return String(format: "%02d:%02d", prodMinutes, prodSeconds)
}
#IBAction func playerButton1(_ sender: UIButton) {
}
#IBAction func playerButton2(_ sender: UIButton) {
}
#IBAction func unwindToVC1(sender: UIStoryboardSegue) {
if let settingsController = sender.source as? SettingsController {
playerTimer1.text = settingsController.storedTime
playerTimer2.text = settingsController.storedTime
storedTime = settingsController.storedTime
}
}
}
To keep things simple, here's the code. I have removed the functions that aren't relevant to the discussion - non-implemented buttons, etc.,
The simple idea is that you use numbers (Int / Double / whatever) to store variables that you are counting with, and then have helper functions to present the variable in different formats
import UIKit
class ChessTimer: UIViewController {
var timer = Timer()
var isTimerRunning = false
var storedTime : Int = 0 // use an integer to store the time
override func viewDidLoad() {
super.viewDidLoad()
if isTimerRunning == false {
runTimer()
}
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self,selector: (#selector(ChessTimer.updateTimer)),userInfo: nil, repeats: true)
isTimerRunning = true
}
#objc func updateTimer() {
// because we're storing the value in an Int, we don't need to convert it here
storedTime -= 1
if storedTime < 1 {
timer.invalidate()
// use the helper function to format the result for zero time as well as everything else
// that way, if you ever change it, you only have to change in one place
playerTimer1.text = prodTimeString(0)
playerTimer2.text = prodTimeString(0)
}
else {
playerTimer1.text = prodTimeString(storedTime)
}
}
func prodTimeString(_ time: Int) -> String {
// because there's only one parameter, and it's obvious, add '_' then you don't need the label when you call it
let prodMinutes = time / 60 % 60
let prodSeconds = time % 60
return String(format: "%02d:%02d", prodMinutes, prodSeconds)
}
}

updating label variables from a running timer

I'm trying to create a counter that does simple math when the timer reaches zero. I have my timer working somewhat correctly, and the labels displaying the original variables, but they are not updating as the timer hits "zero". Where am I going wrong in my code?
class ViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var goldCounter: UILabel!
#IBOutlet weak var turnCounter: UILabel!
var seconds = 15
var timer = Timer()
var gold = 1000
var turns = 1
func updatelabels () {
goldCounter.text = String(gold)
turnCounter.text = String(turns) }
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer),userInfo: nil, repeats: true)
}
#objc func updateTimer() {
seconds -= 1
timerLabel.text = String(seconds)
if seconds == 0 {
seconds = 15}
}
func increaseGold () {
if seconds == 1 {
gold = gold + 1000
turns = turns + 1
}}
override func viewDidLoad() {
super.viewDidLoad()
self.runTimer()
goldCounter.text = String(gold)
turnCounter.text = String(turns)
// Do any additional setup after loading the view, typically from a nib.
func increaseGold () {
if seconds == 1 {
gold = gold + 1000
turns = turns + 1
}}
func updatelabels () {
goldCounter.text = String(gold)
turnCounter.text = String(turns) }
}
}
Your main issue is that you aren't calling updateLabels to update your labels.
I would suggest using property observers (didSet) to set your labels as the values change instead of relying on a separate function to change them.
Also, you need to call increaseGold in your timer handler:
class ViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var goldCounter: UILabel!
#IBOutlet weak var turnCounter: UILabel!
var seconds = 15 {
didSet {
timerLabel.text = String(seconds)
}
}
var timer = Timer()
var gold = 1000 {
didSet {
goldCounter.text = String(gold)
}
}
var turns = 1 {
didSet {
turnCounter.text = String(turns)
}
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer),userInfo: nil, repeats: true)
}
#objc func updateTimer() {
seconds -= 1
if seconds == 0 {
seconds = 15
}
increaseGold()
}
func increaseGold () {
if seconds == 1 {
gold = gold + 1000
turns = turns + 1
}
}
override func viewDidLoad() {
super.viewDidLoad()
seconds = 15
gold = 1000
turns = 1
self.runTimer()
}
}
I think when you added let, your problem is gone. Delete var timer = Timer(). It is not neccessary
func runTimer() {
let timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer),userInfo: nil, repeats: true)
}

Unable to stop timer through another viewcontroller, Swift

I developing a timer app for an apple watch.
I have two different Views at the moment. One with the actual timer (TimerController) and another with a pause-button (SwipeController).
I'm trying to stop/start the timer in the TimerController with the action from the button in the SwipeController.
Problem is that the timer stops, but the timer will not start again after hitting the button the second time.
If I press the button one time, the timer stops. If i press it again two times the timer will start again but will not stop when hitting the button again.
Any ideas of what the problem could be?
TimeController
import WatchKit
import Foundation
import UserNotifications
class TimerController: WKInterfaceController {
#IBOutlet weak var timerOutlet: WKInterfaceTimer! //
#IBOutlet weak var simple_timer_label: WKInterfaceLabel!
var myTimer : Timer?
var duration : TimeInterval = 1 //arbitrary number. 1 seconds
var isPaused = false //flag to determine if it is paused or not
var elapsedTime : TimeInterval = 0.0 //time that has passed between
var number_as_a_timer:Int = 0
var startTime = NSDate()
var dim_date = Date()
var current_minute: Int = 0
var current_hour: Int = 0
var curent_second: Int = 0
var seperate_is_paused_bool: Bool = false
override func awake(withContext context: Any?) {
super.awake(withContext: context)
start_timer()
}
func timeString(time:TimeInterval) -> String {
let hours: Int = Int(time) / 3600
let minutes: Int = Int(time) / 60 % 60
let seconds: Int = Int(time) % 60
let com = NSDateComponents()
com.minute = minutes
com.second = seconds
com.hour = hours
dim_date = NSCalendar.current.date(from: com as
DateComponents)!
self.timerOutlet.setDate(dim_date)
self.timerOutlet.start()
return String(format:"%02i:%02i:%02i", hours, minutes, seconds)
}
func start_timer() {
myTimer = Timer.scheduledTimer(timeInterval: duration, target:
self,selector: #selector(timerDone), userInfo: nil, repeats:
true)
}
#objc private func timerDone(){
//timer done counting down
if !isPaused {
number_as_a_timer += 1
let output:String = self.timeString(time:
TimeInterval(number_as_a_timer))
self.simple_timer_label.setText(output)
print(output)
}
}
override func willActivate() {
super.willActivate()
NotificationCenter.default.addObserver(self, selector:
#selector(stop_timer(notification:)), name: .stopTimer, object:
nil)
}
#objc func stop_timer(notification:NSNotification) {
// Timer is paused. so unpause it and resume countdown
if isPaused {
myTimer = Timer.scheduledTimer(timeInterval: 1,
target:self, selector: #selector(timerDone), userInfo: nil,
repeats: true)
self.isPaused = false
print("timer paused: resumming1")
} else {
isPaused = true
print("stoping timer")
//get how much time has passed before they paused it
let paused = NSDate()
elapsedTime += paused.timeIntervalSince(startTime as Date)
//stop watchkit timer on the screen
timerOutlet.stop()
//stop the ticking of the internal timer
myTimer!.invalidate()
}
}
}
extension Notification.Name {
static let stopTimer = Notification.Name("stopTimer")
}
SwipeController
import WatchKit
import Foundation
import UserNotifications
class SwipeController: WKInterfaceController {
//#IBOutlet weak var myTimer: WKInterfaceTimer!
var timer = TimerController()
var status: Bool = false
override func awake(withContext context: Any?) {
super.awake(withContext: context)
}
#IBAction func PauseButton() {
if timer.myTimer == nil {
print("timer is nil or invalidated")
print("Y: \(timer.isPaused)")
let userInfo = ["stop": true] as [String: Bool] // you
could also transfer data
NotificationCenter.default.post(name: .stopTimer, object:
nil, userInfo: userInfo)
} else {
print("empty block")
}
}
}
it looks like you aren't ever actually checking for you isPaused boolean to be true or false in your if statement when checking if your timer is paused.
if isPaused { <-----------
myTimer = Timer.scheduledTimer(timeInterval: 1,
target:self, selector: #selector(timerDone), userInfo: nil,
repeats: true)
self.isPaused = false
print("timer paused: resumming1")

Timer.scheduledTimer not executing selector function

I created a app to check whether an integer is prime. It works fine, however I wanted to add a timer to time how long it takes to calculate some of the bigger primes. I created a timer variable but the Timer.scheduledTimer doesn't execute the addSecond function. What am I doing wrong? Thanks
class ViewController: UIViewController {
var number = 0.0
var timer = Timer()
var time = 0
func addSecond() {
time += 1
}
#IBOutlet weak var txtNumber: UITextField!
#IBOutlet weak var lblOutput: UILabel!
#IBAction func btnCalculate(_ sender: Any) {
print("calculating")
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.addSecond), userInfo: nil, repeats: true)
var i = 1
var divs = 0
number = Double(txtNumber.text!)!
while i <= Int(number)/2 {
if number.truncatingRemainder(dividingBy: Double(i)) == 0 {
divs += 1
}
i += 1
}
if divs > 1 {
lblOutput.isHidden = false
lblOutput.text = String(number) + " is not prime"
timer.invalidate()
} else {
lblOutput.isHidden = false
lblOutput.text = String(number) + " is prime. It took " + String(time) + " seconds to calculate it"
timer.invalidate()
}
}

Running a Timer that counts down in background when app isnt the focus? Swift

I want my countdown timer to suspend and then resume when the app leaves / returns to focus, using the time away to calculate how much time should be deducted.
I am using the app delegate file (not sure thats the right location? or if they are meant to be in the view controllers file as functions of their own?)
Issue is im getting a lot of errors such as:
Value of type 'AppDelegate' has no member 'restTimer'
Use of unresolved identifier 'nextFireDate'
Use of unresolved identifier 'selector'
restTimer was declared as a timer in my view controllers file but when i tried these blocks in that file i got an equal number of errors for unresolved identifiers
and using the following 2 code blocks
func applicationWillResignActive(_ application: UIApplication) {
guard let t = self.restTimer else { return }
nextFireDate = t.fireDate
t.invalidate()
and
func applicationDidBecomeActive(_ application: UIApplication) {
guard let n = nextFireDate else { return }
let howMuchLonger = n.timeIntervalSinceDate(NSDate())
if howMuchLonger < 0 {
print("Should have already fired \(howMuchLonger) seconds ago")
target!.performSelector(selector!)
} else {
print("should fire in \(howMuchLonger) seconds")
Timer.scheduledTimerWithTimeInterval(howMuchLonger, target: target!, selector: selector!, userInfo: nil, repeats: false)
}
}
UPDATE: Added full views code due to issue incorporating the answer
import Foundation
import UIKit
class RestController: UIViewController {
#IBOutlet weak var restRemainingCountdownLabel: UILabel!
#IBOutlet weak var setsRemainingCountdownLabel: UILabel!
#IBOutlet weak var numberOfSetsLabel: UILabel!
#IBOutlet weak var numberOfRestLabel: UILabel!
#IBOutlet weak var adjustSetsStepper: UIStepper!
#IBOutlet weak var adjustRestStepper: UIStepper!
var startDate: Date!
let startDateKey = "start.date"
let interval = TimeInterval(20)
var restTimer: Timer!
var restCount = 0
var setCount = 0
var selectedTime = 1
var selectedSets = 1
private let resignDateKey = "resign.date"
#IBAction func endSetPressed(_ sender: Any) {
if (setCount > 0){
setCount -= 1
setsRemainingCountdownLabel.text = String(setCount)
}
handleTimer()
}
#IBAction func setStepperValueChanged(_ sender: UIStepper) {
numberOfSetsLabel.text = Int(sender.value).description
self.setCount = Int(sender.value)
self.selectedSets = setCount
setsRemainingCountdownLabel.text = String(setCount)
}
#IBAction func restStepperValueChanged(_ sender: UIStepper) {
numberOfRestLabel.text = Int(sender.value).description
let timeMinSec = timeFormatted(totalSeconds: Int(sender.value)*60)
restRemainingCountdownLabel.text = timeMinSec
self.selectedTime = Int(sender.value)
restCount = self.selectedTime * 60
}
#IBAction func resetSetsButton(_ sender: Any) {
setCount = Int(adjustSetsStepper.value)
setsRemainingCountdownLabel.text = String(setCount)
}
override func viewDidLoad() {
super.viewDidLoad()
numberOfSetsLabel.text = String(selectedSets)
numberOfRestLabel.text = String(selectedTime)
createTimer(interval: interval)
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc private func willResignActive(notification: Notification) {
print("resigning")
guard restTimer.isValid else {
UserDefaults.standard.removeObject(forKey: startDateKey)
return
}
restTimer.invalidate()
UserDefaults.standard.set(Date(), forKey: startDateKey)
}
#objc private func didBecomeActive(notification: Notification) {
print("resume")
if let startDate = UserDefaults.standard.object(forKey: startDateKey) as? Date {
let elapsed = -startDate.timeIntervalSinceNow
print("elpased time: \(elapsed) remaining time: \(interval - elapsed)")
if elapsed > interval {
timerUp()
} else {
createTimer(interval: interval - elapsed)
}
}
}
private func createTimer (interval: TimeInterval) {
restTimer = Timer.scheduledTimer(withTimeInterval: interval , repeats: false) {[weak self] _ in
self?.timerUp()
}
startDate = Date()
}
private func timerUp() {
print("At least \(interval) seconds has elapsed")
}
func handleSets() {
if (setCount > 0) {
self.restCount = self.selectedTime * 60
}
handleTimer()
}
func handleTimer() {
if (restTimer?.isValid ?? false) {
restTimer?.invalidate()
restTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(RestController.updateTimer), userInfo: nil, repeats: true)
} else {
restTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(RestController.updateTimer), userInfo: nil, repeats: true)
}
}
func updateTimer() {
if (restCount > 0){
restCount -= 1
} else if (restCount == 0){
restTimer?.invalidate()
}
restRemainingCountdownLabel.text = timeFormatted(totalSeconds: restCount)
}
func timeFormatted(totalSeconds: Int) -> String {
let seconds: Int = totalSeconds % 60
let minutes: Int = (totalSeconds / 60) % 60
return String(format: "%02d:%02d", minutes, seconds)
}
I think you cannot rely on the fact that the app will remain in memory while it is in the background. Thus, you should archive all the data you need to recreate the timer at the exact point.
For example, in applicationWillResignActive
UserDefaults.standard.set(value: t.nextFireDate forKey:"NextFireDate")
and in applicationWillEnterForeground
if let fireDate = UserDefaults.standard.object(forKey: "NextFireDate") {
// setup a timer with the correct fire date
}
You don't have to use the AppDelegate for this because it also posts notifications. You can use the AppDelegate if you want. Here is code using notifications:
class ViewController: UIViewController{
private let startDateKey = "start.date"
private let interval = TimeInterval(20)
private var startDate: Date!
private var timer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
createTimer(interval: interval)
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc private func willResignActive(notification: Notification) {
print("resigning")
guard timer.isValid else {
UserDefaults.standard.removeObject(forKey: startDateKey)
return
}
timer.invalidate()
UserDefaults.standard.set(Date(), forKey: startDateKey)
}
#objc private func didBecomeActive(notification: Notification) {
print("resume")
if let startDate = UserDefaults.standard.object(forKey: startDateKey) as? Date {
let elapsed = -startDate.timeIntervalSinceNow
print("elpased time: \(elapsed) remaining time: \(interval - elapsed)")
if elapsed > interval {
timerUp()
} else {
createTimer(interval: interval - elapsed)
}
}
}
private func createTimer (interval: TimeInterval) {
timer = Timer.scheduledTimer(withTimeInterval: interval , repeats: false) {[weak self] _ in
self?.timerUp()
}
startDate = Date()
}
private func timerUp() {
print("At least \(interval) seconds has elapsed")
}
}