Why my timer resets the first time the app goes background? - ios

In my SpriteKit project when the user starts a new game, the timer starts counting and, every new game, only the first time the app goes background, the timer resets, then the next times works fine(pauses and when the app goes foreground it continues counting), how can I avoid this first time reset?
This is how I handle the timer:
override func didMove(to view: SKView) {
DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
self.myTimer()
})
}
func myTimer() {
run(.wait(forDuration: 1), completion: {
self.timer += 1
print(self.timer)
if(self.timer == 5){
self.goldBottle = true
}
self.myTimer()
})
}

Related

how to stop func update(_ currentTime: TimeInterval) in Spritekit

override func update(currentTime: NSTimeInterval) {
// ...
}
how to stop currentTime in update method?
I want to calculate time since game starts.
when player clicks pause button, current scene is paused successfully.
but, currentTime in update is moving.
When the scene is paused, update will not be called, try this code:
class GameScene: SKScene {
override func didMove(to view: SKView) {
let wait = SKAction.wait(forDuration: 2)
let pause = SKAction.run { self.isPaused = true }
self.run(SKAction.sequence([wait, pause]))
}
override func update(_ currentTime: TimeInterval) {
print("update")
}
}
"update" will be printed for the first 2 seconds only. This proves that pausing the scene will stop update. You are probably not pausing the scene, but a node in the scene on which all actions are run.
In addition to that, implementing a pause screen by pausing the scene isn't such a good idea because the user can't leave the pause screen by tapping if the scene is paused. Also, you can't show cool animations in the pause screen.
What I usually do is to have a background node. Every game sprite are added as a child of the background node. In the pause screen, the background pauses but the scene is still running. I then add the pause screen spirte as a direct child of the scene so that you can still interact with the pause screen.
You don't really need to "stop" the update method. You just need to check whether the game is paused in the method. If it is, return immediately.
If you want to measure how much time has passed since the player started the game, you can also use Date objects. Create a Date at the start of the game and another Date when the user pauses. Call the timeIntervalSince method and there you go!
I've implemented pause like the following, I think the code is self-explenatory but I've added some comments:
// BaseScene inherits from SKScene but adds some methods, e.g. for dealing with
// controller input.
class GameScene : BaseScene {
// The previous update time is used to calculate the delta time.
// The delta time is used to update the Game state.
private var lastUpdateTime: NSTimeInterval = 0
override func update(currentTime: CFTimeInterval) {
// NOTE: After pausing the game, the last update time is reset to
// the current time. The next time the update loop is entered,
// a correct delta time can then be calculated using the current
// time and the last update time.
if lastUpdateTime <= 0 {
lastUpdateTime = currentTime
} else {
let deltaTime = currentTime - lastUpdateTime
lastUpdateTime = currentTime
Game.sharedInstance.update(deltaTime)
}
}
// A method on BaseScene that is called when the player presses pause
// on controller.
override func handlePausePress(forPlayer player: PlayerIndex) {
paused = true
lastUpdateTime = 0
}
}
My Game singleton only makes use of the delta time (time difference since last call) when updating the game state.

SWIFT Infinite animation/function call loop [duplicate]

This question already exists:
SWIFT Updating screen in between steps of while loop
Closed 6 years ago.
I'm building an app that simulates Conway's Game Of Life. I'm trying to run an infinite animation when a RUN button is pressed. Here's my code:
//When RUN button is clicked, call run repeat
#IBAction func run(sender: AnyObject) {
UIView.animateWithDuration(3, delay: 2, options: [.Repeat], animations: {self.runrepeat()}, completion: nil)
}
//Run repeat calls run, calculating next generation of the board
func runrepeat() {
board.run()
//Update the appearance of all the buttons based on their new values
for cellButton in self.cellButtons {
cellButton.setTitle("\(cellButton.getLabelText())",
forState: .Normal)
}
}
When the RUN UI button is pressed, I want run() to be called, which should continuously call runrepeat() every 3 seconds. board.run() runs algorithms to determine the configuration of the next generation of cells, and the for cellButton{} loop updates that appearances of all the cells.
However, as is, runrepeat() is only called once, so the next generation appears on the board and the animation stops, without any delays. My RUN button is correctly executing runrepeat(), but only once. I want it to repeat forever.
I tried this too:
//Run repeat calls run, calculating next generation of the board
func runrepeat() {
while(true){
board.run()
//Update the appearance of all the buttons based on their new values
for cellButton in self.cellButtons {
cellButton.setTitle("\(cellButton.getLabelText())",
forState: .Normal)
}
}
}
but the infinite while loop just causes my program to freeze up. The updating of the screen never happens.
Can someone please help me with executing a continuous function call, screen update loop? Please this is due in 4 days.
extension NSTimer {
static public func schedule(delay delay: NSTimeInterval, handler: NSTimer! -> Void) -> NSTimer {
let fireDate = delay + CFAbsoluteTimeGetCurrent()
let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, 0, 0, 0, handler)
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes)
return timer
}
static func schedule(repeatInterval interval: NSTimeInterval, handler: NSTimer! -> Void) -> NSTimer {
let fireDate = interval + CFAbsoluteTimeGetCurrent()
let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, interval, 0, 0, handler)
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes)
return timer
}
}
Then, you can call UI updates every 3 seconds like so:
NSTimer.schedule(repeatInterval: 3.0, handler: { timer in
//UI updates
})

problems with Timer on Swift

I'm having a problem with my timer on swift. When I press the play button once it works fine and adds one second every second but when I play it twice it adds 2 seconds every one second and so on. This is my code.
var timer = NSTimer()
var time = 0
var minute = 0
#IBOutlet var timerLabel: UILabel!
func increasedTimer()
{
time++
timerLabel.text = "\(minute)" + "m " + "\(time)" + "s"
if time == 59
{
minute++
time = -1
}
}
#IBAction func play(sender: AnyObject)
{
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("increasedTimer"), userInfo: nil, repeats: true)
}
#IBAction func reset(sender: AnyObject)
{
timer.invalidate()
time = 0
minute = 0
timerLabel.text = "0m 0s"
}
#IBAction func pause(sender: AnyObject)
{
timer.invalidate()
}
Every time you tap play you create and start an additional timer.
You have a few options to fix this.
From a user experience point of view, you need to enable/disable your three buttons (Play, Pause, Stop) appropriately. It makes no sense that the user can tap Play a 2nd time while the timer is going. And of course the Pause and Stop buttons shouldn't be enabled until Play has been tapped.
So start by fixing the user interface so the buttons make sense. Once that is done, you won't have the problem in your current code since the user won't be able to tap Play twice without first pausing or stopping.
In the short term, every time you play put another timer.invalidate() before starting a new timer. Definitely better to disable the play button.
button.setBackgroundImage(backgroundImage1, forState: .Normal)
button.setBackgroundImage(backgroundImage2, forState: .Highlighted)
button.setBackgroundImage(backgroundImage2, forState: .Disabled)
Examples of things you can do. You can supply different images for a greyed out play button, a selectable play button. Etc. Then change the forState. More options available then shown.
Or you can just change the text shading in the button with:
button.enabled = true
button.enabled = false

Swift - UIProgressView is not smooth with NSTimer

So I am using an NSTimer to let the user know the app is working. The progress bar is set up to last 3 seconds, but when running, it displays in a 'ticking' motion and it is not smooth like it should be. Is there anyway I can make it more smooth - I'm sure just a calculation error on my part....
If anyone could take a look that would be great. Here is the code:
import UIKit
class LoadingScreen: UIViewController {
var time : Float = 0.0
var timer: NSTimer?
#IBOutlet weak var progressView: UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
// Do stuff
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector:Selector("setProgress"), userInfo: nil, repeats: true)
}//close viewDidLoad
func setProgress() {
time += 0.1
progressView.progress = time / 3
if time >= 3 {
timer!.invalidate()
}
}
}
Edit: A simple 3 second UIView animation (Recommended)
If your bar is just moving smoothly to indicate activity, possibly consider using a UIActivityIndicatorView or a custom UIView animation:
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
UIView.animateWithDuration(3, animations: { () -> Void in
self.progressView.setProgress(1.0, animated: true)
})
}
Make sure your progressView's progress is set to zero to begin with. This will result in a smooth 3 second animation of the progress.
Simple animated progress (Works but still jumps a bit)
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIProgressView_Class/#//apple_ref/occ/instm/UIProgressView/setProgress:animated:
func setProgress() {
time += 0.1
progressView.setProgress(time / 3, animated: true)
if time >= 3 {
timer!.invalidate()
}
}
Option with smaller intervals. (Not recommended)
Set your timer to a smaller interval:
timer = NSTimer.scheduledTimerWithTimeInterval(0.001, target: self, selector:Selector("setProgress"), userInfo: nil, repeats: true)
Then update your function
func setProgress() {
time += 0.001
progressView.setProgress(time / 3, animated: true)
if time >= 3 {
timer!.invalidate()
}
}
For continues loader
timer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(setProgress), userInfo: nil, repeats: true)
and
func setProgress() {
time += 0.001
downloadProgressBar.setProgress(time / 3, animated: true)
if time >= 3 {
self.time = 0.001
downloadProgressBar.progress = 0
let color = self.downloadProgressBar.progressTintColor
self.downloadProgressBar.progressTintColor = self.downloadProgressBar.trackTintColor
self.downloadProgressBar.trackTintColor = color
}
It's hard to say exactly what the problem is. I would like to see the output if you put a print line in setProgress to print a timestamp. Is it actually firing every tenth of a second? My guess is that it is not.
Why not? Well, the timer schedules a run loop task in the main thread to execute the code in setProgress. This task cannot run until tasks in front of it in the queue do. So if there are long running tasks happening in your main thread, your timer will fire very imprecisely. My first suggestion is that this is perhaps what is happening.
Here is an example:
You start a timer to do something every second.
Immediately after, you start a long running main thread task (for example, you try to write a ton of data to a file). This task will take five seconds to complete.
Your timer wants to fire after one second, but your file-writing is
hogging the main thread for the next four seconds, so the timer can't fire
for another four seconds.
If this is the case, then to solve the problem you would either need to move that main thread work to a background thread, or else figure out a way to do it while returning to the run loop periodically. For example, during your long running main thread operation, you can periodically call runUntilDate on your run loop to let other run loop tasks execute.
Note that you couldn't just increment the progress bar fill periodically during the long running main thread task, because the progress bar will not actually animate its fill until you return to the run loop.
What about proper way for animating changes: animateWithDuration:animations: or CABasicAnimation. You can use this for creating smooth animations

swift - how use FIRE() in NSTimer

I need a cycle of 5 times of 2.5 seconds in a single cycle, however, that the pressure of a button bait from single cycle without interrupting the scheduling.
I use method fire (with pressed button) for stoping a repeating timer without interrupting its regular firing schedule, but I don't know, subsequent cycles are shorter than the defined 2.5 seconds:
.......
#IBAction func buttonZeroPressed(sender: AnyObject) {
timer.fire()
++addWin
}
......
timer = NSTimer.scheduledTimerWithTimeInterval(2.5, target: self,
selector: Selector("levelOne"), userInfo:nil,repeats: true)
......
func levelOne() {
level = 1
count++
changePhoto()
if (count >= 5) {
timer.invalidate()
}
}
After the 3 pressed button, the timer seems to be 2.5 seconds, and I do not understand why!
The fire() method is how you start a timer that's been created but not scheduled. You want to use invalidate() to stop the timer.
#IBAction func buttonZeroPressed(sender: AnyObject) {
timer.invalidate()
++addWin
}
Your timer will start immediately since you call scheduledTimerWith.... If you wanted to create a timer without starting, you would use:
timer = NSTimer(timeInterval: 2.5, target: self, selector: Selector("levelOne"), userInfo:nil,repeats: true)

Resources