I created a timer and set it to repeat every 30 seconds
the timer waits 30 seconds to run the first time, then another 30 seconds for the second time, I wanted it to run the first time without the 30 seconds, is there any way?
Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { timer in
if iteration >= 0 {
runCommands()
iteration -= 1
if iteration == 0 { exit(0) }
}
}
Just call fire. It does what the name implies.
Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { timer in
if iteration >= 0 {
runCommands()
iteration -= 1
if iteration == 0 {exit(0)}
}
}.fire()
The problem is that you have no reference to your timer. Make one!
let timer = Timer.scheduledTimer...
Now in the next line you can say timer.fire(). This causes the timer to execute its block immediately.
Either refactor the code that's called from the timer closure to be a function, or put the closure into a variable. Then invoke the function/closure explicitly:
typealias TimerClosure = (_: Timer?) -> Void
let timerClosure: TimerClosure = { [weak self] timer in
guard let self = self else { return }
if self.iteration >= 0 {
self.runCommands()
self.iteration -= 1
if self.iteration == 0 {exit(0)}
}
}
Timer.scheduledTimer(withTimeInterval: 30, repeats: true, block: timerClosure )
timerClosure(nil)
}
Edit:
The approach above will work, but as others pointed out, you can also call fire() on your timer manually.
I'll leave the above part of the answer since it shows how to set up a local variable containing a closure and then both pass it to a function and call it directly, but that's not really needed here. Instead, just do this:
let timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] timer in
guard let self = self else { return }
if self.iteration >= 0 {
self.runCommands()
self.iteration -= 1
if self.iteration == 0 { exit(0) }
}
}
timer.fire()
Related
I have attached my code for you to see.
What I want to achieve:
Timer repeats 5 times
Hardcoded time of 10s
the display of a number (i) reduces by 1 with each iteration
What happens:
i goes straight to 1
countdown goes something like 5, 0, -2, -4, -8, -13 NOT 10,9,8,7,6,5,4,3,2,1,0 then repeat
var timer = Timer()
var i = 5
// number of repeats is 5
var time = 10
// timer is 10s
class ViewController: UIViewController {
#IBOutlet weak var displayTime: UILabel!
#IBOutlet weak var displayI: UILabel!
#IBAction func startButton(_ sender: Any) {
for _ in 1...5 {
displayI.text = String(i)
countdown()
i -= 1
}
}
func countdown() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Action), userInfo: nil, repeats: true)
}
#objc func Action() {
time -= 1
displayTime.text = String(time)
if time == 0 {
timer.invalidate()
}
}
Can anyone help?
(I also tried a repeat while loop where it repeats while (i >= 1) so it should stop when i=0)
Remember that loops (any kind) runs very quickly. Your for _ in 1...5 loop completes before the first call to Action even starts. This is because countdown() completes almost immediately, not after the timer it creates has been invalidated.
When you create a timer, it runs asynchronously to the rest of your code. So the line immediately after countdown() is not when the timer will end. The line in the if time == 0 statement is.
Note that even if the timer were synchronous, your code wouldn't have worked because you didn't reset time to 10 when each timer ends.
Anyway, the five timers will decrement time almost at the same time, and depending on when the UI refreshes, you will see that time has decremented a different amount.
You seem to just want to count from 10 to 0 five times. You can do this with just one timer:
#objc func Action() {
time -= 1
displayTime.text = String(time)
if time == 0 {
i -= 1
time = 11
}
if i == 0 {
timer.invalidate()
}
}
#IBAction func startButton(_ sender: Any) {
countdown()
}
i counts how many times this is, and time counts the number to display.
Is it possible to use DispatchQueue to create multiple delays in my code?
For example, with sleep I can do:
for index in 1...20 {
do {
sleep(1)
// Code I want to run every 1 second for 20 times
}
}
completionHandler()
But I want to use DispatchQueue instead:
for index in 1...20 {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// Code I want to run every 1 second for 20 times
}
}
completionHandler()
which doesn't seem to be working (it's not waiting 1 second each time)
Edit:
At the end of running the code for 20 times, I want to call the completionHandler to return a result of the code in the loop. Problem is that this does not wait for the DispatchQueue
// Code I want to run every 1 second for 20 times
What you're looking for is a Timer.
https://developer.apple.com/documentation/foundation/timer
var timer : Timer?
var times = 0
#objc func fired(_ t:Timer) {
times += 1
print("do your code") // do your code here
if times == 20 {
t.invalidate()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self,
selector: #selector(fired), userInfo: nil,
repeats: true)
}
You will see that print("do your code") runs every 1 second 20 times.
You need to add a different delay Double(index)
for index in 1...20 {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(index)) {
// Code I want to run every 1 second for 20 times
}
}
My code is here:
#IBAction func updateTime(){
let inputVal: String = textMinutes.text!
var inputInt = Int(inputVal)
timerLabel?.text = "\(inputInt)"
if inputInt != 0 {
inputInt -= 1
}
else {
endTimer()
}
So on the inputInt -=1 I keep getting the error "Cannot convert value of type 'Int?' to expected argument type 'inout Int'". I am not sure how to fix this or if my code is even correct here. This is all for this:
func startTimer() {
countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(ViewController.updateTime), userInfo: nil, repeats: true)
}
which is for the timer I am trying to make. I am pretty new to this so if there is anything else you need to see I can post it. I've looked all over for solutions but I have not found anything. Thanks in advance.
Your logic is bad. You don't want to change text of some label every second according to same other text every time.
You should create global variable for time, let's say interval
class ViewController: UIViewController {
var interval = 0
...
}
then when you start timer, make sure that user wrote number and if does, start timer and set interval
func startTimer() {
if let interval = Int(textMinutes.text!) {
self.interval = interval
timerLabel?.text = String(interval)
countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(ViewController.updateTime), userInfo: nil, repeats: true)
} else {
... // handle case if user didn’t write integer
}
}
then every time timer updates, just decrease interval and change text of your text field by this interval variable, then when interval is 0, invalidate your timer
#IBAction func updateTime() {
interval -= 1
timerLabel?.text = String(interval)
if interval == 0 { endTimer() }
}
I'm trying to create a delay inside a while loop. I'm fairly new to this and it's currently just not working. It never fires even once with the dispatch delay, but if I remove the delay it fires repeatedly.
Basically what I'm doing is checking if the velocity of nodes in a SKScene is still moving, if they're still moving, don't end the game. But once they've slowed down, end the game.
func RemainingNodeCheck (complete:() -> Void) {
CountVelocites()
if (IdleVelocity.max()!.isLess(than: 1.0)) {
complete()
} else {
print("Velocity too high, begin wait...")
while !(IdleVelocity.max()?.isLess(than: 1.0))! {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(1)) {
print("Second passed")
self.CountVelocites()
}
if (IdleVelocity.max()!.isLess(than: 1.0)) {
break
}
}
print("Velocity Calmed down")
complete()
}
}
I believe this might be something to do with threads? Or it's actually just telling the delay to begin waiting for one second so many times that it never gets to call?
UPDATE: I would use a timer, but the RemaingNodeCheck is being called from another part and it's waiting for RemainingNodeCheck to send back complete()
You never want to "wait". But you can set up a repeating timer that checks for some condition, and if so, calls the complete closure (invalidating the timer, if you want). E.g.
class ViewController: UIViewController {
var idleVelocity: ...
weak var timer: Timer?
deinit {
timer?.invalidate()
}
func startCheckingVelocity(complete: #escaping () -> Void) {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
guard let self = self, let maxVelocity = self.idleVelocity.max() else { return }
if maxVelocity < 1 {
timer.invalidate()
complete()
return
}
print("velocity too high...")
}
}
}
In Swift 3 I have a loop which can be canceled by user pressing a button. Within the loop some checks are made. After the check, the task can sleep for a minute. But when calling the task with
let delayQueue = DispatchQueue(label: "com.myApp.queue3", qos: .utility)
let additionalTime: DispatchTimeInterval = .seconds(3)
repeat {
delayQueue.asyncAfter(deadline: .now() + additionalTime) { self.update() }
} while !self.stop
the loop itself needs to run all the time waiting for the user
"stop", indicates, that user clicked on stop button.
Is that waste of CPU power? How could I avoid this loop to be done all the time?
You should use Timer instead.
var timer: Timer?
let timeInterval: TimeInterval = 3
func didPressCancelButton() {
timer?.invalidate()
}
func beginUpdates() {
timer = Timer.scheduledTimer(
timeInterval: timeInterval,
target: self,
selector: #selector(self.update),
userInfo: nil,
repeats: true
);
}
func update() {
print("Updated")
}
Instead of delaying execution in thread with an outer loop you can put your loop in thread instead and make it to sleep.
import Foundation
class YourUpdatingClass {
private let updateQueue: OperationQueue
init() {
updateQueue = OperationQueue()
updateQueue.name = "com.myApp.queue3"
updateQueue.qualityOfService = .utility
}
private var updateOperation: BlockOperation?
#IBAction func startUpdating() {
guard updateOperation == nil else {
// In case if updating already started
return
}
updateOperation = BlockOperation { [weak self] in
while true {
Thread.sleep(forTimeInterval: 3)
self?.update()
}
}
updateQueue.addOperation(updateOperation!) // we just created updateOperation, so we can use `!`, but use it with caution
}
#IBAction func stopUpdating() {
updateOperation?.cancel()
updateOperation = nil
}
private func update() {
print("update") // Whatever your update does
}
}
You updating is contained in eternal while loop which takes a nap every 3 seconds.
Stopping is managed by cancelling operation, instead of checking some var in the loop.