I would like to change from scene to scene after a certain amount of time on one scene in Swift. I am trying to create a survival type game that the player has to survive a level for so long before they can advance to the next level.
I know that after I get to func being called I will be using this to go to the next scene.
self.view?.presentScene(GameScene2())
I am sure that something along the lines of NSTimer is going to be used, but anything that can be given to point me in the right direction would be of great help. Thank you in advance.
An NSTimer is one option. Depending on what sort of accuracy you need and how long your duration will be you may or may not want to use an NSTimer. Example of an NSTimer ...
var myTimer = NSTimer()
func startMyTimer() {
myTimer = NSTimer.scheduledTimerWithTimeInterval(1.5, target: self, selector: "myFunction", userInfo: nil, repeats: true)
//1.5 is the time interval that you want to call the timer, in this case every 1.5 seconds
//the selector calls myFunction which is the function that you want to call every time the timer reaches its time interval
//if you want the timer to repeat and call myFunction() every 1.5 seconds then repeat should be true. if you only want it to be called once then repeat should be false
}
func myFunction(){
//do whatever i am supposed to do when the timer reaches my specified interval
}
Now this may not be solution you are looking for. Another way is to use GCD's (grand Central Dispatch) dispatch_after . A very nice and extremely easy to use function can be found here, dispatch_after - GCD in swift? , in the second answer down, compliments of stackoverflow's #matt
When your view is instantiated, you want to begin an NSTimer. See this page for help creating this: How can I use NSTimer in Swift?. This page may help as well: Using an NSTimer in Swift.
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(30.0, target: self, selector: "update", userInfo: nil, repeats: true)
Then, you can just put the
self.view?.presentScene(GameScene2())
in a func update like so:
func update() {
self.view?.presentScene(GameScene2())
Hope this helps. If so, please check off my answer. If not, comment and I will try and help again.
Related
I'm trying to create an on-screen animation for iOS with Swift 4 on the Xcode IDE by drawing an object to the screen, waiting, adjusting the object's location, and redrawing. However, when I do this inside of a for loop, the screen simply waits and then draws the object in the final position. Why is it doing this? How can I animate the way that I want to? Is there an alternative way to animate that I should be considering?
My code is here:
var counter = 0
for n in 0...10{
counter = counter + 1
draw_wheel(counter)
sleep(1)
}
I simply see the object I am trying to draw in its final orientation after 10 seconds.
If you want to create step by step animation over the time, you should consider to use CADisplayLink instead of Timers or loops.
Basically CADisplayLink is a timer object that allows your application to synchronize its drawing to the refresh rate of the display and draw according to timestamp.
func createDisplayLink() {
let displaylink = CADisplayLink(target: self,
selector: #selector(step))
displaylink.add(to: .current,
forMode: .default)
}
#objc func step(displaylink: CADisplayLink) {
print(displaylink.timestamp)
}
Problem:
I have like function to update like in my post.But if a user hits like function fast or continuously .
My UI update of like status is overridden by next function call and causes bug in like counter.
Question:
How can I stop another function call to override the execution of previous function call?
P.s: I guess sample code is not needed.
Thanks in advance.
I guess the easiest way is to use a flag variable, and you can set a timer to reset your flag variable later if needed like:
var flag = false
func test() {
if flag == false {
// your code ...
print("123")
}
flag = true
}
// Set this later
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector("some function"), userInfo: nil, repeats: false)
I have multiple views each associated with its own timer. However, only one view is running a time each time. I am accessing each view in a for ... in loop and if the previous timer in the previous view has stopped, which I invalidate in the selector method, I want to fire the time for the next view.
the code looks like this:
var timer = NSTimer()
let timeInterval:NSTimeInterval = 1
var views = [TimerView]()
var timeCountDown: NSTimeInterval = 0
override func viewDidLoad() {
//viewDidLoad create the necessary UIView and fill the views array then pass them to a call to startTimer:
startTimer(views)
}
//startTimer function create the countdown timer
func startTimer(timerViews: [TimerView]){
for timerView in timerViews {
if !timer.valid { // a view with its associated timer start only when the previous timer has run its course and is invalidated
//creating a timer to associate with the current view
timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "timerDidEnd:",
userInfo: timerView,
repeats: true)
}
}
}
//Callback function
func timerDidEnd(timer: NSTimer){
let timerView = timer.userInfo as! TimerView
timeCountDown = timeCountDown - timeInterval
timerView.timeLabel.text = timeToString(timeCountDown)
timerView.curValue = CGFloat(timeCountDown)
if timeCount <= 0.0 {
resetTimeCount()
}
}
func timeToString(time:NSTimeInterval) -> String {
let minutes = Int(time) / 60
let seconds = time - Double(minutes) * 60
return String(format:"%02i:%02i",minutes,Int(seconds))
}
func resetTimeCount(){
timer.invalidate()
}
With some print debugging for three views I am getting this output: "running the for loop", "calling view #1 by for loop", "calling the timer", "calling view #2 by for loop", "calling view #3 by for loop","countdown starting" ... That is the countdown only start after the for loop has terminated.
the problem I have is that the first timer run while the for loop calls all the views in my view array. How do I get the for loop to wait for the timer to be invalidated before it iterates to the next view?
What I am trying to achieve is this: the first view starts a countdown; when that countdown finished a second appears and run a countdown as well. Then the third view with a new countdown and so on until the last view. But it looks like the for loop in my code finish looping before the first timer associated with the first view in the collection actually starts
thanks
edit: I am wondering if the timer is not running on a different thread than the for loop?
Your code has a logic problem. It looks like you have a single instance variable, timer. Your for loop starts, and on the first pass, presumably timer is not valid. So you overwrite it with a new timer that IS valid. Now on the second pass through the array of timerViews, you check the same shared timer instance variable, but this time (And all subsequent times) timer is valid, so the body of the if statement doesn't fire.
If you really want a separate timer for each view then you will need to store all those timers. One way would be to have an array of timers that goes along with your array timerViews. Or you could create an array of tuples where each element contains a view and it's associated timer.
Stepping back from your problem, though, why do you want a separate timer for each field? Why not have a single timer, and each time it fires, loop through the array of fields and decide what you need to do with each one? You could have an array of structs that contains references to your views plus status information that lets your loop decide what to do with each one.
Swift newbie here. I have two questions:
First, how do I create a timer which AUTOMATICALLY counts down when a ViewController scene is opened? My problem is that the NSTimer ENDS automatically when the scene is opened.
For instance, whenever I go to the said scene, the TimerLabel says: “Time’s Up!”
Before my second question, below is my tweaked code from: makeapppie.com/…/swift-swift-using-nstimer-to-make-a-timer…/
var timer = NSTimer()
let timeInterval:NSTimeInterval = 0.05
let timerEnd:NSTimeInterval = 120.0 //Timer should end in 120 seconds
var timeCount:NSTimeInterval = 0.0
func timerDidEnd(timer:NSTimer){
timeCount = timeCount - timeInterval
if timeCount <= 0 {
TimerLabel.text = "Time's Up!"
timer.invalidate()
}
}
func timeString(time:NSTimeInterval) -> String {
let minutes = Int(time) / 60
let seconds = time - Double(minutes) * 60
return String(format:"%02i:%02i",minutes,Int(seconds))
}
func StartTimer() { // Function called in viewDidLoad
if !timer.valid{
TimerLabel.text = timeString(timeCount)
timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "timerDidEnd:",
userInfo: nil,
repeats: true)
}
}
My second question is this: I do not want to use any UISwitch to choose whether to have the timer counting up, or counting down. I need a COUNTING DOWN timer only which AUTOMATICALLY counts down when view is opened. How do I do that?
Please take note that my time format is: “minutes:seconds” as described in the timeString function.
Please help.
Welcome to SO. I think you're in over your head here.
A timer doesn't count up or count down. It fires on a regular interval. You write a method that gets called each time the timer fires.
Note that 0.05 is a pretty short interval for a timer. Timers are not super-accurate, so you might be trying to get too much out of them.
Your code is a confused mess. You start timeCount out at 0. Your timer method, that gets called repeatedly, is called timerDidEnd even though you want your timer to keep running. Your timerDidEnd method subtracts "timeInterval" (.05 seconds) from timeCount when the timer fires, which makes timeCount negative. You then check to see if timeCount is less than 0 (it will be the very first time through this code) and if it is, you invalidate the timer. You also never do anything to display the value of timeCount.
If you want code that counts down, you need to start out with a value that's larger than 0, and subtract a small amount from it each time the timer fires. Then you need to do something with that timeCount value. If you're not displaying it somewhere, it really isn't doing anything useful.
P.S. On an unrelated subject, the avatar pictures on SO are square. You should post a square avatar picture (or create a square crop to post.) When you post a rectangular picture it gets really stretched-out and looks bad.
My problem is that the NSTimer ENDS automatically when the scene is opened.
It sounds like your putting your NSTimer and the code that manages it in a view controller. In that case, it's not surprising that the timer goes away when that view controller does. If the timer should persist from one view controller to the next, then you should manage it from an entirely separate object from the view controller. Since you don't want the timer object to have to keep track of all the view controllers that could possibly be interested in it, have it post notifications that interested view controllers can listen to.
I am trying to create a new stopwatch timer.
I have my:
Main Timer ViewController
a timer Model - where I store my timer.
When my timer finishes in my model how can I let the main ViewController know that it's been completed?
Ideally I would like to run a function the main UIViewController when the timer is completed in the model.
Class myTimer: NSObject {
func stopTimer() {
timer?.invalidate()
print("timer Stopped at \(currentTime)")
}
}
I'd do this by posting, registering and deallocating notifications.
-registering for notifications, do this in viewDidLoad of viewController.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "timerExpired", name: "TimerCompleteNotification", object: nil)
where selector is the function to call when the timer complete notification is posted.
-posting notifications. Add this in whatever function updates the timer label and calls this when the timer expires.
NSNotificationCenter.defaultCenter().postNotificationName("TimerCompleteNotification", object: nil)
Just make sure "TimerCompleteNotification" is the spelled the same in both posting and addObserver. What you are doing here is saying, "I'd like to observe a notification that may happen, and when it does, I'll call this function," And then when the timer ends, you post that notification and perform the designated function.
Forgot to tell you to add this to your view controller or you will get an access error
deinit{
//Unregister for notificationsd
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Have your MyTimer class do exactly the kind of thing an NSTimer already does. The "user" of MyTimer registers a callback function when it creates / starts MyTimer, and the MyTimer calls that function when it fires.