swift early function exit swift without guard - ios

I'm looking for a way to exit a function without using guard. After extensive searching, I cannot find a way to exit a function and call the next at the same time when a button it pressed.
The button calls a repeat using a selector:
#IBAction func BottomLeft(sender: AnyObject) {
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(0.01), target: self, selector: "bottomLeftMovement", userInfo: nil, repeats: true)
}
This is when the code button is pressed, and the ball will follow this movement:
func bottomLeftMovement() {
Ballx = Ballx - 0.6125
Bally = Bally + 1.2
self.Ball.center.x = Ballx
self.Ball.center.y = Bally
}
I am looking to make it so that when a different button is pressed, that this function will be exited and the corresponding one will be called.
I cannot hard program into the first function, as it is an interchangeable thing, as opposed to a function chain.
Any help would be much appreciated.

What your code is doing is creating a new timer every time you tap on the button. This is not the correct way to do this. I'd recommend you do some research/reading on game development.
But basically, if you're gonna do things this way (which again, is not a good thing (tm)). You need to keep track of your timer in a property, and invalidate it (to stop it). Read about timers too (https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/)
So, in short:
- don't do this
- if you must, track your timer in a property
- invalidate() your timer and start a new one as required
Note: Track and invalidate the timer like this:
(note I'm doing this from memory, not tested, but something along the lines of...)
class someClass {
var myTimer = NSTimer?
func myFunction() {
if let timer = myTimer {
timer.invalidate()
}
timer = NSTimer(...

Related

how to create countdowns in swift that will start after a countdown has finished

[New to Swift]
I am making an app on Swift 5 that displays time left before my next task is to start.
My app:
Task 1: Start at 9.30 am
Task 2: Start at 10.15 am
Let's say Current Time: 09.00 am
Countdown: Time before next task start = 00:30:02 --> 00:30:01 ...
I would like to display countdown of the nearest task.
Currently, I can only have one countdown timer on my viewonLoad() that picks up the current countdown. It continues and once it finishes it does not start next timer after it has finished. I understand I have to deal with Background state at a later date, but since I am slowly starting. My idea is to make sure I can initiate next countdown timer once my current one has expired.
So at any point when I open my app, it will always display countdown till my next task.
var count = 30 // calculation simplified ... this data is calc from two different date obj
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#objc func update() {
if(count > 0) {
count = count - 1
countdownLabel.text = String(count)
}else{
timer!.invalidate()
getNextTaskTimes()
}
}
func getNextTaskTimes(){
count = 5 // not working
}
How can I achieve this please? Also, is there a better way of doing this? Given that, my user may choose to close the app and come back at a later time, I still would want my countdown to continue in the background.
Any help is appreciated. :D Thanks
I realized the above code works. I just need to make sure I do not invalidate the timer in the else block. Invalidating the timer just kills it.

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.

How to repeat every X seconds in Swift?

I am trying to build a simple single view app which runs an infinite slide show of a selection of images with a time delay between each image change.
The code I wrote for this is below. I tried to put this into viewDidLoad and viewDidAppear but the screen remained blank which I guess is because the function never finishes due to the infinite loop.
I learnt a bit of Python before iOS and with tkinter, your code would go into the mainloop. But I am not quite sure how to do the equivalent in Swift
Could someone please explain why I am having this problem and how to do this in Swift. Thanks.
var arrayimages: [UIImage] = [UIImage(named: "charizard")!,UIImage(named:"Flying_Iron_Man")!]
var x: Int = 0
var images: UIImage
let arraycount = arrayimages.count
repeat{
images = arrayimages[(x % arraycount)]
sleep(1)
slideshow.image = images
x++
} while true
NB: slideshow is an image view outlet.
You're looking for NSTimer
let timer = NSTimer.scheduledTimerWithTimeInterval(
1.0, target: self, selector: Selector("doYourTask"),
userInfo: nil, repeats: true)
The first argument is how frequently you want the timer to fire, the second is what object is going to have the selector that gets called, the third is the selector name, the fourth is any extra information you want to pass as a parameter on the timer object, and the fifth is whether this should repeat.
If you want to stop the code at any future point:
timer.invalidate()
Create a repeating NSTimer:
var timer = NSTimer.scheduledTimerWithTimeInterval(2.0,
target: self,
selector: "animateFunction:",
userInfo: nil,
repeats: true)
Then write a function animateFunction:
func animateFunction(timer: NSTimer)
{
//Display the next image in your array, or loop back to the beginning
}
Edit: Updated for modern Swift versions: (>= Swift 5)
This has changed a lot since I posted this answer. NSTimer is now called Timer in Swift, and the syntax of the scheduledTimer() method has changed. The method signature is now scheduledTimer(timeInterval:target:selector:userInfo:repeats:)
Also, the way you create a selector has changed
So the call would be
var timer = Timer.scheduledTimer(timeInterval: 2.0,
target: self,
selector: #selector(animateFunction(_:)),
userInfo: nil,
repeats: true)
And the animateFunction might look like this:
func animateFunction(timer: Timer)
{
//Display the next image in your array, or loop back to the beginning
}

Subsequent WKInterfaceTime / NSTimer events with array of NSTimeInterval values

I am trying to run subsequent Timer events with WKInterfaceTimer & NSTimer, the problem is that I cannot figure out a way to make more than two subsequent calls with one NSTimer object. Basically, I would like run timer to complete then fire up the next.
Here's some sample code that hopefully explains my idea a little better....
1) I am firing off the first timer in awakeWithContext:
func initalTimer() {
let timer1String = NSMutableAttributedString(string: "Lap1")
runStatusLabel.setAttributedText(timerString)
myTimer = NSTimer.scheduledTimerWithTimeInterval(duration, target: self, selector: Selector("timerDone"), userInfo: nil, repeats: false)
runTimer.setDate(NSDate(timeIntervalSinceNow: duration))
runTimer.start()
}
NOTE: Everything works great at this point, then the tiemrDone function is called where I then fire off another timed event.
2)
func timerDone() {
//print("Done")
elapsedTime = 0.0
myTimer!.invalidate()
startTime = NSDate()
timeRunning = false
// Call second timed event
timer2() // just another NSTimer / WKInterfaceTimer function
}
"Stacking" the functions with a completionHandler does not seem to help OR most likely I am doing something wrong...
func execute_Timers(timeInterval: NSTimeInterval, completionHandler: (success: Bool, error: String?) -> Void ) -> Int {
// Code below never gets executed
}
I haven't tested this, and it is just a guess: When your timerDone() method is called, you invalidate the timer. Therefore it doesn't "complete," so your completion routine isn't called. When your timer completes, it gets invalidated anyway, so the call should not be needed. Try removing:
myTimer!.invalidate()
and see what happens.
Thanks for the reply, and you are quite correct - I do not need to call myTimer!.invalidate(). The solution that worked for me was to have different timerDone methods and conditionaly call the next time method.
-Paul

Swift - slowing down "too fast" animation (UIImage updates) -- aka is NSTimer the only option here?

Newbie to IOS programming - learning through Swift. I'm writing a simple "slot machine / dice game".
I'm trying to show the user a flashing sequence of rolls before the "actual" roll appears.
func doFancyDiceRoll() {
for x in 1...100 {
Die1.image = PipsImg[RollOne()]
Die2.image = PipsImg[RollOne()]
Die3.image = PipsImg[RollOne()]
}
}
Die1, etc., are defined as generic UIImage views.
In any case, I'm not seeing the 100x iterations, just the images at the end of the loop. I'm assuming that either it redraws "too fast" or that IOS is trying to be smart, and only draws the last images so as to conserve resources.
I'll wildly guess that I need to either implement some kind of delay here, or, IOS needs to be told to explicitly draw out my images, and not try to outthink my intent.
For the delay, I've seen something about NSTimer, but nothing I saw seems to simply say something like "pause for .05" second, and the whole construct was unclear as they were ObjC examples/conversions.
(Note: I've simplified things here --- normally, I would store the value of RollOne() so I can use it later. I also would like to make an array (or collection?) like Die[1].image, but that is another question.)
========== Edit =======
OK, so I'm following up with more of my original code, merged in with that of #doctordoder so we can discuss a bit better. Hopefully that is kosher. (If this appended question is too long, please advise me on the best way to post a lengthy follow-up directly.)
import UIKit
class ViewController: UIViewController {
//( stripping out stuff unneeded for discussion )
// refers to same label below - works but kosher??
#IBOutlet var btnRoll_x: UIView
#IBAction func btnRoll(sender: AnyObject) {
triggerRoll()
}
var timer : NSTimer? = nil
var rolls : Int = 0
func triggerRoll() {
//hiding is bad UI, but until i know how to "disable & dim"
btnRoll_x.hidden = true
timer = NSTimer.scheduledTimerWithTimeInterval(0.10, target: self, selector: "doFancyDiceRoll", userInfo: nil, repeats: true);
}
func doFancyDiceRoll() {
Die1.image = PipsImg[randomInt(6)]
Die2.image = PipsImg[randomInt(6)]
Die3.image = PipsImg[randomInt(6)]
if (++rolls > 10)
{
timer?.invalidate()
timer = nil
rolls = 0 // DCB added this presumed missing line
btnRoll_x.hidden = false //again, need to do this better
}
}
}
Hopefully, my implementation of the code is what would have been intended. I made some minor adjustments for (hopeful) clarity.
Good news is I have working code. I have just enough understanding to get in place, but I'm fuzzy on some details.
Here is what I (think I) know...
We declare an NSTImer object, and a roll counter at the main level of the class. I note that in my original version, I had the roll counter scoped within the rolling function itself. Took me a while to understand why it could not live in the DiceRoll loop itself, but now I do. I'm going to express it poorly, but since the timer is going to call DiceRoll multiple instances, it needs to live outside the function.
The button btnRoll gets touched, and invokes triggerRoll().
To prevent the user from touching the button while we are in progress, which put us into a state where the roll counter never got to zero, I hide the button. (I'll figure how to properly put in in disabled state later.)
The timer is set. It fires every .1 second (within limits), and is set to repeat. (until .invalidate ). And it "calls" the function doFancyDiceRoll via the selector: attribute.
So, the big change as previously noted is that doFancy..Roll() no longer loops. It excites a single instance up updating the images. It checks the counter, and if we reach the limit, we kill the timer, which stops the timer (invalidate). (And I unhide the button, making it available again.)
So, a few things I am concerned/wondering about: I get the value of timers for other things that need to happen periodically (take health away every second, check a GPS position every 10 seconds, etc.). It's seems a odd construct to force a screen refresh.
Frankly, I would have expected to see see something like this:
func doFancyDiceRoll() {
for x in 1...100 {
Die1.image = PipsImg[RollOne()] // and 2 and 3 of course.....
VIewController.forceRedraw <<=== something like this, or maybe
ViewController.wait(.05) <<== this?? I dunno ;-)
}
}
instead we end up with about 20 extra lines or so. I'd be interested in knowing if there other approaches that could work keeping the loop intact.
Anyway, assuming this is the one true way to go, I guess my followup to this is how do I pass parameters, since this is not a "real" function call. Trying
selector: "doFancyDiceRoll(40)"
was not objected to by the IDE, but failed in execution.
I had exactly same problem back in days, entire loop is finished before the view is refreshed as #doctordoder mentioned. Solved with using NSTimer
var rollCount = 0
let arrayOfImages = ["image01", "image02", "image03"]
var timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("doFancyDiceRoll"), userInfo: nil, repeats: true)
func doFancyDiceRoll() {
if rollCount == 100 {
timer.invalidate
rollCount = 0
{
else {
//get images from array arrayOfImages[rollCount]
rollCount++
}
}
there could be typos, since I have no Xcode right now.
I have basically the same answer as above :(, but I thought I'd post it anyway.
var timer : NSTimer? = nil
var rolls : Int = 0
func doFancyDiceRoll() {
timer = NSTimer.scheduledTimerWithTimeInterval(0.10, target: self, selector: "roll", userInfo: nil, repeats: true);
}
func roll() {
println("rolling")
Die1.image = PipsImg[RollOne()]
Die2.image = PipsImg[RollOne()]
Die3.image = PipsImg[RollOne()]
if (++rolls > 100)
{
timer?.invalidate()
timer = nil
}
}
Rather than NSTimer and invalidating, you can use dispatch_after to do the work for you.
func rollDice(howManyTimes: Int) {
die1.image = PipsImg[RollOne()]
die2.image = PipsImg[RollOne()]
die3.image = PipsImg[RollOne()]
if howManyTimes > 0 {
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(Double(NSEC_PER_SEC) / 10.0))
dispatch_after(delayTime, dispatch_get_main_queue()) {
self.rollDice(howManyTimes - 1)
}
}
}
This will run the code for the number of times specified, delaying each time by 0.1 seconds. It works like this: First it sets the images on each die, then, if there are more iterations, it does a dispatch_after to call itself with rollDice(howManyTimes - 1)
With this, you don't need to maintain a NSTimer and it is pretty self contained.

Resources