Swift: How to make a method to be called every 10 seconds - ios

In my main ViewController ViewDidLoad, I have the following code that calls the "tick" method every second.
self.timerTick = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(ViewController.tick), userInfo: nil, repeats: true)
In the "tick" method, I check if a boolean value is true every second, and if true, I need to call a second method called SpeakText(), but I want to call the SpeakText() only once every 10 seconds. How do I do this?

Create a class property to keep track of the time since the last SpeakText. Update it each time tick is called, and reset it to 0 when SpeakText is called:
var secondsSinceSpeakText = 10
func tick() {
secondsSinceSpeakText += 1
if boolFlag && secondsSinceSpeakText >= 10 {
SpeakText()
secondsSinceSpeakText = 0
}
}
Note: Starting secondsSinceSpeakText with an initial value of 10 will allow the first SpeakText to happen immediately.

Just create another timer for speaktext when and when you call your speak text function fire it. If its under 10 seconds return false

Related

How do you make a timer controller with a condition?

I'm a beginner in Swift and have a task to change the bottom sheet message when the process of the app doesn't work in three minutes. So, the message will change from "available" to "not available" if the process does not work.
I found code syntax like:
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
What I think:
var waktu = 0
Timer.scheduledTimer(withTimeInterval: 180.0, repeats: false) {
if waktu == 180 {
timer.invalidate()
//run the change message function
}
}
You can add observer on your property, so it will invalidate the timer when condition met, like so:
var waktu = 0 {
didSet {
if waktu == 180 {
timer.invalidate()
}
}
}
Your code creates a timer that will fire once in 3 minutes.
You’re using a variation on the timer method you list, scheduledTimer(withTimeInterval:repeats:block:)
That variant takes a closure instead of a selector. Then you’re using “trailing closure syntax” to provide that closure in braces outside the closing paren for the function call. That’s all good.
However, you define a variable waktu And give it a starting value of 0. You don’t show any code that will change that value, so your if statement if waktu == 180 will never evaluate to true and run your change message function. You need to update that if statement to determine if “the process of the app works”, whatever that means. (Presumably you have some definition of what your app working/not working means, and can update that if statement accordingly.)

How to show Multiple timers at same screen using one function in Swift ios [duplicate]

This question already has an answer here:
Show Multiple Counter in label but at same screen (Swift, IOS)
(1 answer)
Closed 5 months ago.
In my app I have spots on which a user can tap to unlock them, a user can unlock multiple spots at the same time and there can be different number of spots every time, whenever a user unlocks a spot i want to show a timer of 60 seconds on the unlocked spot, So, there can be multiple timers on same screen all having different counts. For example if a user unlocks 2 spots with the difference of 30 seconds. the timer on spot 1 should be 30 seconds left and spot 2 should be 60 seconds left. I only have 1 function for doing this task I can easily show timer on 1 spot but showing them on multiple places, multiple times is very complex. I am sharing a picture too for reference. any help would be appreciated.
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
#objc func updateCounter() {
if counter > 0 {
let time = Int(counter/60)
let dec = Int(counter.truncatingRemainder(dividingBy: 60))
spotTiming = String(time) + ":" + String(dec)
}
counter -= 1
}
i want to show timers where counter is written
You can use a timer as a trigger to update the UI every x period (example: 0.5s). You save time remaining every spots at a variable as Array and reduce each element when timer was triggered.
let step = TimeInterval(0.5)
timer = Timer.scheduledTimer(timeInterval: step, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
var timer_remainings = [TimeInterval]()
#objc func updateCounter() {
for (index, item) in timer_remainings.enumerated() {
timer_remainings[index] = max(0.0, timer_remainings - step)
}
// Update UI
updateUI()
}

Multiple timers at once ios

I am making an app where the user can have multiple timers going at once and see them in a list view.
I am aware that there are 2 main options for working out time:
Subtract the date started from current date (current date-start date)
OR
Use an NSTimer and take away 1 second every second from each active timer.
I have previously been using the latter, but having looked around the internet I am starting to think that the data one may be better.
Please could you let me know which you think is best to use, and if you chose the first one (dates), please could you provide some sample code on how to use it.
You can Use an NSTimer and take away 1 second every second from each active timer. You can use this class.
class CustomTimer {
typealias Update = (Int)->Void
var timer:Timer?
var count: Int = 0
var update: Update?
init(update:#escaping Update){
self.update = update
}
func start(){
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerUpdate), userInfo: nil, repeats: true)
}
func stop(){
if let timer = timer {
timer.invalidate()
}
}
/**
* This method must be in the public or scope
*/
#objc func timerUpdate() {
count += 1;
if let update = update {
update(count)
}
}
}
To use multiple timer you can create multiple instance of CustomTimer, Example Code:
let timer1 = CustomTimer { (seconds) in
// do whatever you want
}
timer1.start()
let timer2 = CustomTimer { (seconds) in
// do whatever you want
}
timer2.start()
NOTE:
timerUpdate method will be called exactly at 1 second interval. to keep some space for function execution we can set interval to 0.9 or 0.95 according to time taken by execution.
You use both. You have one Timer that repeats every second. The handler for the Timer then iterates through your list of start dates for each of the user's timers and you update the display for each based on the current date.

Time Took Variable in Swift

Overview: I am trying to create quiz game where someone has to answer questions in a certain amount of time. They have 10 seconds to solve each question or it will move to the next controller. The problem is that whenever I click next question before the full time is up it keeps counting down to 10. What should happen is it should stop counting time for that question.
So far I have implemented the following code to try to make this work. var time = 0 and first created a global variable called time. Then I created timer code as following, var count = 10 #IBOutlet var countDownLabel: UILabel!
Then I create a label that will tick down every second until it reaches 0. Code as follows:
if(count > 0) {
countDownLabel.text = String(count)
count -= 1
time += 1
}
print (time)
if (count <= 0 ) {
count = 10
self.performSegue(withIdentifier:"NextQuestion1", sender: nil)
}
Summary: Everytime count goes down time should go up. Then at the end it will display the time they. I would gladly appreciate some help on this problem.
When you click on the next button, you need to invalidate the timer so that it will stop counting.
Say you created your timer in this way
let timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(YourSegueFunction), userInfo: nil, repeats: false)
Then when you click on the next button, use the following line of code to stop the timer
timer.invalidate()
Here is the demo

Intermittent Missing Seconds with Timer

I have a timer, which is a singleton, that repeatedly fires every second. I allow the user to pause the timer as well as resume. I am keeping track of the start date of the timer, and I am subtracting any pauses from the elapsed time.
Unfortunately, I can't seem to fix an intermittent issue where pausing and resuming the timer causes a skipping of one second.
For instance, in the following code block, I start the timer and print the seconds:
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
10.0
11.0
12.0
13.0
14.0
15.0
16.0
17.0
18.0
19.0
In the following code block, I resume the timer, printing the seconds again. However, as you can see, the 20th second has not been printed:
21.0
22.0
23.0
24.0
25.0
26.0
I cannot seem to figure out where I am losing the second. It does not happen with each pause and resume cycle.
The properties that I am using to keep track of the aforementioned are as follows:
/// The start date of the timer.
private var startDate = Date()
/// The pause date of the timer.
private var pauseDate = Date()
/// The number of paused seconds.
private var paused = TimeInterval()
/// The number of seconds that have elapsed since the initial fire.
private var elapsed = TimeInterval()
I start the timer by creating the timer and setting the start date:
/// Starts the shower timer.
func startTimer() {
// Fire the timer every second.
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateElapsedSeconds), userInfo: nil, repeats: true)
// Set the start time of the initial fire.
startDate = Date()
}
If the user pauses the timer, then the following method executes:
/// Pauses the shower timer.
func pauseTimer() {
// Pause the timer.
timer?.invalidate()
// Set the timer to `nil`, according to the documentation.
timer = nil
// Set the date of the pause.
pauseDate = Date()
}
Then, the following method executes when the user resumes the timer:
/// Resumes the timer.
func resumeTimer() {
// Recreate the timer.
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateElapsedSeconds), userInfo: nil, repeats: true)
// Add the number of paused seconds to the `paused` property.
paused += Date().timeIntervalSince(pauseDate)
}
The following method, which is called by the method that executes when the timer fires, sets the number of elapsed seconds since the initial fire, less the sum of any pauses:
/// Sets the number of elapsed seconds since the timer has been started, accounting for pauses, if any.
private func updateElapsedTime() {
// Get the date for now.
let now = Date()
// Get the time that has elapsed since the initial fire of the timer, and subtract any pauses.
elapsed = now.timeIntervalSince(startDate).rounded(.down).subtracting(paused.rounded(.down))
}
Finally, the following method is the Selector that executes when the timer fires:
/// Updates the number of elapsed seconds since the timer has been firing.
#objc private func updateElapsedSeconds() {
// Configure the elapsed time with each fire.
updateElapsedTime()
// Post a notification when the timer fires, passing a dictionary that includes the number of elapsed seconds.
NotificationCenter.default.post(name: CustomNotification.showerTimerFiredNotification, object: nil, userInfo: nil)
}
What am I doing incorrectly to cause a missing second intermittently?
So the issue here is that Timer is not accurate in this way. Or rather, its timekeeping is reasonably accurate, but the actual rate of firing has some variance as it is dependent on the runloop.
From the documentation:
A timer is not a real-time mechanism; it fires only when one of the
run loop modes to which the timer has been added is running and able
to check if the timer’s firing time has passed.
To show this, I got rid of all of the rounding in your code and printed the output (you don't even need to pause to see this happen). Here is what this variance looked:
18.0004420280457
19.0005180239677
20.0004770159721
21.0005570054054
21.9997390508652
23.0003360509872
24.0003190040588
24.9993720054626
25.9991790056229
Sometimes it fires particularly late and this causes the whole thing to get thrown off. The rounding doesn't help because you are still depending on the timer for the actual reference time and eventually it will be off by more than a second.
There are a few ways to fix the situation here depending on what exactly you are trying to accomplish. If you absolutely need the actual time, you can adjust the timer to fire at fractions of a second and instead use that output to estimate the seconds a little more accurately. This is more work and will still not be totally right (there will always be a variance).
Based on your code, it seems like simply incrementing a number based on the timer should be enough to accomplish your goal. Here is a simple modification to your code making this work. This will count up simply and never skip a second in the count whether you pause or not:
/// The number of seconds that have elapsed since the initial fire.
private var elapsed = 0
private var timer: Timer?
/// Starts the shower timer.
func startTimer() {
elapsed = 0
// Fire the timer every second.
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateElapsedSeconds), userInfo: nil, repeats: true)
}
/// Pauses the shower timer.
func pauseTimer() {
// Pause the timer.
timer?.invalidate()
// Set the timer to `nil`, according to the documentation.
timer = nil
}
/// Resumes the timer.
func resumeTimer() {
// Recreate the timer.
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateElapsedSeconds), userInfo: nil, repeats: true)
}
/// Sets the number of elapsed seconds since the timer has been started, accounting for pauses, if any.
private func updateElapsedTime() {
// Get the time that has elapsed since the initial fire of the timer, and subtract any pauses.
elapsed += 1
// debug print
print(elapsed)
}
/// Updates the number of elapsed seconds since the timer has been firing.
#objc private func updateElapsedSeconds() {
// Configure the elapsed time with each fire.
updateElapsedTime()
// Post a notification when the timer fires, passing a dictionary that includes the number of elapsed seconds.
NotificationCenter.default.post(name: CustomNotification.showerTimerFiredNotification, object: nil, userInfo: nil)
}

Resources