So I've got two timers, one that increases the score and one that spawns enemies. I used a notification to invalidate the timers, and then I'm using another one to recreate the timers. When I quit and then open the app, there are two sets of enemies being spawned on top of each other. I think timerRecreate = true and also the regular timers in GameScene are also being called.
GameViewController.swift file:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("pauseTimers:"), name:UIApplicationWillResignActiveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("startTimers:"), name:UIApplicationDidBecomeActiveNotification, object: nil)
}
func pauseTimers(notification : NSNotification) {
println("Observer method called")
timer.invalidate()
scoretimer.invalidate()
}
func startTimers(notification : NSNotification) {
println("Observer method called")
timerRecreate = true
}
Code for timers in GameScene.swift
override func didMoveToView(view: SKView) {
//Spawn timer for enemy blocks
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("spawnEnemies"), userInfo: nil, repeats: true)
//Timer for keeping score
scoretimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("scoreCounter"), userInfo: nil, repeats: true)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if timerRecreate == true {
//Spawn timer for enemy blocks
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("spawnEnemies"), userInfo: nil, repeats: true)
//Timer for keeping score
scoretimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("scoreCounter"), userInfo: nil, repeats: true)
timerRecreate = false
}
}
I think the problem is when you initially open the app, be that after quitting out of it or opening it for the first time, timerRecreate is set to true as well as the regular spawning of blocks so two sets of blocks are spawned at the same time. How can I fix this?
Fixed it! I hope...
Anyway heres what I did. I created another boolean called timerz and set it to false when the DidBecomeActive notification was made. At the same time, timeRecreate is set to true. This guarantees that both timer sets arent running at the same time. In the update function inside the if statement on whether timRecreate was true, I set timeRecreate to false and timerz to true. So the timers are recreated and then it switches back to to old way of spawning them. I also put this old method of spawning them inside an if statement on whether timerz was true or false.
Related
I want to use below lines but where should I write it?
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(function), userInfo: nil, repeats: true)
It depends what you want to achieve. If you want that slider automatically start when app loads, then put it in viewDidLoad().
If you want to start slider when you press some button, then you need to put this code inside the #IBAction function for that button.
Note: #objc function which is determining work of the slider is written separately, like in the example above.
var i=Int()
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(imageChange), userInfo: nil, repeats: true)
// Do any additional setup after loading the view.
}
#objc func imageChange(){
self.imageView.image=images[i]
if i<images.count-1{
i+=1
}
else{
i=0
}
}
I have the following code:
class firstVC: UIViewController {
var timer : Timer?
func scheduledTimerWithTimeInterval(){
timer = Timer.scheduledTimer(timeInterval: 60, target: self,
selector: #selector(self.anotherFunc), userInfo: nil, repeats:
true)
}
override func viewDidAppear(_ animated: Bool) {
scheduledTimerWithTimeInterval()
}
}
I'm trying to stop the timer without success:
func stopTimer() {
if timer != nil {
timer?.invalidate()
timer = nil
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
stopTimer()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopTimer()
}
I even tried to put the stop function in applicationWillResignActive
And in applicationDidEnterBackground but it didn't stop:
firstVC().stopTimer()
Your help will be appreciated, thank you.
As others have said, you are creating multiple timers without killing the old one before starting a new one. You need to make sure you stop any current timer before starting a new one.
What I do is to make my timers weak. I then use the code `myTimer?.invalidate() before trying to create a new timer:
class firstVC: UIViewController {
weak var timer : Timer?
func scheduledTimerWithTimeInterval(){
timer?.invalidate()
timer = Timer.scheduledTimer(timeInterval: 60, target: self,
selector: #selector(self.anotherFunc), userInfo: nil, repeats:
true)
}
}
By making your timer weak, the only thing that keeps a strong reference to the timer is the run loop. Then, when you invalidate it, it immediately gets released and set to nil. By using optional chaining to call the invalidate method, it doesn't do anything if it's already nil, and stops it and causes it to go nil if it IS running.
Note that this approach only works if you create your timer in one shot using one of the scheduledTimer() factory methods. If you try to create a timer first and then add it to the run loop, you have to use a strong local variable to create it or it gets released as soon as you create it.
The problem is your timer gets instantiated more than once, so the original timer loses its reference.
Add a variable didStartTimer = false.
And then in viewDidAppear do a validation, and then call the timer func.
That should do it.
like this:
class firstVC: UIViewController {
var timer : Timer?
var didStartTimer = false
func scheduledTimerWithTimeInterval(){
timer = Timer.scheduledTimer(timeInterval: 60, target: self,
selector: #selector(self.anotherFunc), userInfo: nil, repeats:
true)
}
override func viewDidAppear(_ animated: Bool) {
if !didStartTimer {
scheduledTimerWithTimeInterval()
didStartTime = true
}
}
After research I found a solution,
The only thing that worked for me is to create functions in AppDelegate file and call them when needed,
Here is the code, the timerSwitch function:
func timerSwitch()
{
if (timerStatus) {
checkStateTimer = Timer.scheduledTimer(
timeInterval: 60,
target:self,
selector: #selector(self.yourFunction),
userInfo: nil, repeats: true)
} else {
checkStateTimer?.invalidate()
}
}
func stopTimer()
{
timerStatus = false
timerSwitch()
}
func startTimer()
{
timerStatus = true
timerSwitch()
}
While 'yourFunction' is what you want to execute when the timer starts,
In my case is sending heartbeat.
Then I called the timerSwitch is the following functions in AppDelegate:
func applicationWillResignActive(_ application: UIApplication) {
stopTimer()
}
func applicationDidEnterBackground(_ application: UIApplication) {
stopTimer()
}
func applicationDidBecomeActive(_ application: UIApplication) {
startTimer()
}
As a debugging step, try putting var timer = Timer() in the global space (e.g. at the top of the file below the import statements) to make sure there's only one Timer object being created and referred to. If you have the Timer declaration within a function, you'll make a new timer each time you call that function, which causes you to lose the reference to the old one, and ultimately not stop it.
Nothing works for me, at last using self did the trick!!!
self.atimer?.invalidate()
self.atimer = Timer()
I have this code that is suppossed to make my phone vibrate constantly. but it stops after 1 vibration. Is there something wrong with my timer?
var timer: Timer?
#IBAction func button1(_ sender: UIButton) {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
timer?.invalidate()
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: Selector(("doaction")), userInfo: nil, repeats: true)
}
What did I do wrong? help? I am also using xcode with swift. and have an iphone 7 plus if that matters.
The timer does not stop vibrating after one round. The timer never vibrates.
What the timer does is to call the doaction method repeatedly. But the doaction method does not perform any vibration. Therefore the only vibration is the single one in button1.
The vibration is only happening when you tap the button, you need the vibration to happen in your "doaction" method:
var timer: Timer?
#IBAction func button1(_ sender: UIButton) {
doaction()
timer?.invalidate()
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: Selector(("doaction")), userInfo: nil, repeats: true)
}
func doaction() {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
}
I create and start a self-repeating timer in one of my ViewControllers (let's call it VC1) to have some kind of slideshow of images. When transitioning to any other VC the timer of VC1 appears to keep running as its selector method prints stuff every two seconds. Since this interferes with the timer when returning to VC1 from any other VC I have to remove it at some point.
This is what happens in the console: (runImages() is the timer's selector, the number is the image that should be displayed, as you see its weird...)
I thought the timer would stop once I exit VC1 since I do not save it anywhere. Since this is not the case I thought I might remove the timer when leaving VC1. Is there a method that is being called when VC1 is about to be dismissed?
Another approach I had in mind was removing any timers at the beginning of source code of the other VCs. So, when I enter VC2 I want to check for any timers that are running in the project. Is there a way to do that without making the timer a global variable accessible to all VCs?
Code Reference
This is how I create the timer: (outside a method)
var timer: NSTimer!
Then, in a method I set it:
timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "runImages:", userInfo: nil, repeats: true)
runImage() then increases i and calls changeImage() which transitions my imageView's image to the image named like i.
Thanks in advance :)
Update
I made the timer a global variable that is accessible to every VC. The app starts up in VC1, then I transitioned to VC2. There, I inserted this code: if let t = timer {t.invalidate()} and if timer.valid {timer.invalidate()}. Now that made not difference, the timer's selector method keeps printing stuff...
you should keep a reference to the timer in the viewcontroller that is "using" it...
class ViewController: UIViewController {
var timer: NSTimer?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("timerFired"), userInfo: nil, repeats: true)
}
func timerFired() {
// do whatever you want
}
override func viewWillDisappear(animated: Bool) {
if timer != nil {
timer?.invalidate()
timer = nil
}
super.viewWillDisappear(animated)
}
}
Try this
import UIKit
class ViewController: UIViewController {
let timeInterval: NSTimeInterval = 5 // seconds
// The run loop maintains a strong reference already.
weak var timer: NSTimer?
func startTimer() {
// 1. invalidate previous timer if necessary
timer?.invalidate()
// 2. setup a new timer
timer = NSTimer.scheduledTimerWithTimeInterval(
timeInterval,
target: self,
selector: "timerFired",
userInfo: nil,
repeats: true
)
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
// Need to include #objc marker here
// Function must not be private.
#objc internal func timerFired() {
/*
Perform any UI Updates or whatever...
*/
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
startTimer()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
stopTimer()
}
}
So I have a label that counts 1 every second using an NSTimer. I made an app that has a few moving UIImages. However every time the timer counts one up, the view seemingly reloads and the UIImages go back to their original positions. Furthermore, the UIImages are not where I had placed them in the storyboard (I placed them outside the view so they could move inwards, but when I start the app it just shows them right there already. They move but only for one second then go back to their original positions). Same code works fine on the iPhone but doesn't work on an iPad. I think it has something to do with the constraints because the code is the same. Here's the timer code:
func counting() {
timerCount = timerCount + 1
timerLabel.text = "\(timerCount)"
}
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("counting"), userInfo: nil, repeats: true)
// Do any additional setup after loading the view, typically from a override nib.
}
Here's the code for moving my UIImages:
func MoveWalls() {
FirstTimer = NSTimer.scheduledTimerWithTimeInterval(0.07, target: self, selector: Selector("FirstMoving"), userInfo: nil, repeats: true)
SecondTimer = NSTimer.scheduledTimerWithTimeInterval(0.007, target: self, selector:
Selector("SecondMoving"), userInfo: nil, repeats:true)
ThirdTimer = NSTimer.scheduledTimerWithTimeInterval(0.006, target: self, selector:
Selector("ThirdMoving"), userInfo: nil, repeats:true)
FourthTimer = NSTimer.scheduledTimerWithTimeInterval(0.005, target: self, selector:
Selector("FourthMoving"), userInfo: nil, repeats:true)
}
func FirstMoving() {
First.center = CGPointMake(First.center.x + 1, First.center.y)
}
func SecondMoving() {
Second.center = CGPointMake(Second.center.x - 1, Second.center.y)
}
func ThirdMoving() {
ThirdMoving.center = CGPointMake(Third.center.x, Third.center.y + 1)
}
func FourthMoving() {
Fourth.center = CGPointMake(Fourth.center.x, Fourth.center.y 1)
}
My constraints:
Two buttons that start and end the game (Centered horizontally).
The four UIImages (size ratio)
A timer (Top left) which for some reason resets the view with each count.
I had the exact same problem. I solved it by adding this code for each of the moveable views:
override func viewDidLoad() {
super.viewDidLoad()
self.imageView.translatesAutoresizingMaskIntoConstraints = true
self.imageView2.translatesAutoresizingMaskIntoConstraints = true
}