Objective-C : NSTimer and countdown - objective-c

I know that I should understand Apple's documentation for NSTimer, but I don't! I have also read a lot of questions about it but can not find one that suites my case. Well here it is:
The user enter hour and minutes through textfields. I convert those to integers and display them in a "countDownLabel".
How can i make the count down to zero?
It would be nice to show the seconds which is something that the user didn't imported but i guess it will not be hard to show.
How could somebody stop this procedure with a press of a UIButton?
I know that i am asking a lot...I would be really grateful if someone could help!
int intHourhs;
intHourhs=([textHours.text intValue]);
int intMinutes;
intMinutes=([textMinutes.text intValue]);
int *intSeconds;
intSeconds=0;
NSString *stringTotalTime=[[NSString alloc] initWithFormat:#"%.2i:%.2i:%.2i",intHourhs,intMinutes,intSeconds];
[countDownLabel setFont:[UIFont fontWithName:#"DBLCDTempBlack" size:45]];
countDownLabel.text=stringTotalTime;

First you should calculate what your countdown time is, in seconds and put it in a instance variable (ivar)
NSTimeInterval totalCountdownInterval;
I think in order to keep good accuracy (NSTimer firing can be off by as much as 100ms and errors will add up) you should record the date at which the countdown started, and put it in another ivar:
NSDate* startDate = [NSDate date];
Then you can have a timer firing at regular (here 1 second) intervals calling a method on your class repeatedly
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(checkCountdown:) userInfo:nil repeats:YES];
And in that method you check the elapsed time against the total countdown time and update the interface
-(void) checkCountdown:(NSTimer*)_timer {
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSTimeInterval remainingTime = totalCountdownInterval - elapsedTime;
if (remainingTime <= 0.0) {
[_timer invalidate];
}
/* update the interface by converting remainingTime (which is in seconds)
to seconds, minutes, hours */
}

That's a big order. But start with baby steps. You need to create a method in your class which, when invoked (every N seconds), will update the labels you want updated. Then you arrange for a timer to invoke that method every N seconds.
There are several timer variants you might use, but for this case the most straight-forward is probably NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:.
Write your method to look like - (void)timerFireMethod:(NSTimer*)theTimer and the selector for the timer method above is #selector(timerFireMethod:).

Related

How do I get these numbers "counting" up every 5 seconds ?? (ObjC; Xcode)

I want that my ball gets faster in my game. in pos you can choose the speed of the ball. But I want the ball getting faster every 5th second. pos = CGPointMake(5.0,4.0); after 5 seconds 5.0 should turn into 6.0 and 4.0 into 5.0.
I have a Timer which is named MainInt. MainInt is a counter and it counts the time how long you're playing without loosing.
There's also a label which shows the timer.
(IBOutlet UILabel *seconds; .h)
.m
-(void)viewDidLoad {
[lastTime setHidden:YES];
[super viewDidLoad];
// X Speed Y Speed
pos = CGPointMake(5.0,4.0); // <- these numbers (add 1 each every 5 sec.)
Speedy = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(doThis) userInfo:nil repeats:YES];
}
/*
-(void)doThis {
if(MainInt % 5 == MainInt) //True every 5th second
{
pos = CGPointMake();
}
}
*/
Well, for one thing, MainInt % 5 == MainInt is false after 5 seconds for the rest of eternity. 1-4 are the only values of MainInt that would ever make that true. Consider reviewing how the modulo operator works. Even if you change this to MainInt % 5 == 0, which is correct, you still have to ask yourself why you're checking for anything there regarding an external asynchronous time value. All this will do is, if the timer is not in synch, force it to not do anything every single time it is called because you already set it's delay between calls to 5 seconds. If you want everything to synch up nicely, you should probably have one main NSTimer or CADisplayLink in charge of a game loop where you can call methods that need to update your game's state every frame.
If you really want to do it this way with separate timers for everything and try to keep them in synch with your main time value, then here you go.
//replace timer line with this one
Speedy = [NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:#selector(doThis) userInfo:nil repeats:YES];
//replace body of "doThis" with the following
if(MainInt % 5 == 0) //True every 5th second
{
pos = CGPointMake(pos.x + 1, pos.y + 1);
}
Also, you should follow some sort of naming convention and try to name things more clearly. If pos is the speed of the ball, shouldn't it be named velocity instead of position? What is Speedy, an instance variable? If so, why is it uppercase? Plus, Speedy is one of the most unclear name's I've ever heard; it's not a pet, it's a variable. Same thing for MainInt. In general, reserve uppercase names for class names and use camel case for instance variables, methods and functions. It'll make your code clearer. :)

Generating a random time interval in Cocoa

I am pretty new to Cocoa, but I am trying to put a simple reaction game together. Therefore I need to generate the time intervall of the NSTimer randomly. Currently I have tried the code below.
int randomNumber = rand() %5;
changeColor = [NSTimer scheduledTimerWithTimeInterval:(randomNumber) target:self selector:#selector(changeBackground) userInfo:nil repeats:YES];
If you want resolution finer than 1 second, you should create a double from the random number. Perhaps like this:
int sourceRandom100x = rand() % 500; // i.e. 435
double randomInterval = sourceRandom100x/100.0 // 4.35
[NSTimer scheduledTimerWithTimeInterval:(randomInterval) ...
But the technique will use the same interval for every iteration. If you want a freshly-random interval every time, make the timer not repeat, and inside changeBackground, setup another one (via delegation to a more appropriately named new method, such as -(void) setupRandomBackgroundChangeTimerIfNecessary

Decreasing NSTimer interval every time it runs

I want my NSTimer to speed up each time it's run:
-(void)runtimer {
int delay = delay - 1;
[NSTimer scheduledTimerWithTimeInterval:(delay)
target:self
selector:#selector(timerTriggered:)
userInfo:nil
repeats:YES];
}
But this doesn't work. How can I make the delay keep getting smaller and smaller?
I needed this myself and wrote a component CPAccelerationTimer (Github):
[[CPAccelerationTimer accelerationTimerWithTicks:20
totalDuration:10.0
controlPoint1:CGPointMake(0.5, 0.0) // ease in
controlPoint2:CGPointMake(1.0, 1.0)
atEachTickDo:^(NSUInteger tickIndex) {
[self timerTriggered:nil];
} completion:^{
[self timerTriggered:nil];
}]
run];
This calls -timerTriggered: 20 times, spread out over 10 seconds, with ever-decreasing delays (as specified by the given Bézier curve).
Every time this method is run, you make a new variable called delay, then try to set it to itself minus 1. This is Undefined Behavior (the variable was not initialized to anything), and is likely to result in a garbage value for delay.*
You need to store the delay in an instance variable.
- (void) runTimer {
// You are declaring a new int called |delay| here.
int delay = delay - 1;
// This is not the same |delay| that you have declared in your header.
// To access that variable, use:
delay = delay - 1;
*A sinus infestation by evil-aligned supernatural beings is also a possibility.
You have to declare the delay somewhere, like in the class interface or as a static variable.
Also, create a new timer every time, instead of having it repeat.
int delay = INITIAL_DELAY;
-(void)runtimer{
[NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(delay--) target:self selector:#selector(runTimer:) userInfo:nil repeats:NO];
}
You cannot change the fire interval of the timer once you have created it. If you want a different interval you must invalidate the previous timer (hence you should keep a reference to it), and create a new timer with a different interval.

Get a local variable from a function and implement the variable in another function which can be changed dynamically

my app has a function, it gets a value from a NSTextField and then declare the variable, like this:
- (IBAction)startTimer
//all the other code
int totalTime = secs + hoursInSeconds + minutesInSeconds
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerHandler) userInfo:nil repeats:YES];
then, i want to use the local variable totalTime in another function which processes the NSTimer.
- (void)timerHandler
//all other code
totalTime = totalTime - 1;
//invalidate timer when it reaches 0
if (totalTime == 0.0) {
[timer invalidate];
however, as the variable totalTime is a local variable, i cannot use the value, and i cannot move the code over as NSTimer calls it every 1 sec and as the user may change the variable (and thus redeclaring it).
so, is there any way i can get a local variable from a function and implement the variable in another function which can be changed dynamically? or can i implement a NSTimer countdown by just using one function
You could wrap the value in the timer's userInfo:
NSNumber *totalTimeNumber = [NSNumber numberWithInt:totalTime];
timer = [NSTimer scheduledTimerWithTimeInterval:... target:... selector:... userInfo:totalTimeNumber repeats:...];
Or just make it an instance variable.
Well, here's a fun one that works with local variables, instead of instance variables but only on Mac OS 10.6/iOS 4 and above:
-(IBAction)startTimer:(id)sender
{
// ensure, that the variables we'll capture in the block are mutable
__block int totalTime = ...
__block NSTimer *timer;
void (^timerBlock)() = ^{
if (--totalTime <= 0) { // this comparison is much less fragile...
[timer invalidate];
}
};
// If you'd call timerBlock() at this point you'll crash because timer contains junk!
// However, (since timer is declared as __block) we can give it a meaningful value now and have it updated inside of the block, as well:
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerHandler:) userInfo:timerBlock repeats:YES];
}
-(void)timerHandler:(NSTimer*)timer
{
((void (^)())[timer userInfo])(); // retrieve the block and run it
}
Caveat:
Since I'm sending this from my phone, I am not 100% sure about the cast in timerHandler:. But it's something along this line...
You should be able to omit the cast altogether, but will definitely see a warning then.

Objective C: constantly calling a function

I'm trying to make a pretty simple game and I'm stuck. Basically I want to make a UIImageView appear every 2 seconds. My problem is I can't keep track of cumulative time. Right now I have this:
NSDate *date = [NSDate date];
NSTimeInterval secondsSinceNow = [date timeIntervalSinceNow];
NSLog(#"date = %#", date);
NSLog(#"secondsSinceNow = %f", secondsSinceNow);
It's in my button function so its called when the user taps the button. It returns a decimal number always less than 1. I've tried it in the viewDidLoad method as well as it's own method but neither work.
I think it would work if its in it's own method that is check constantly, but I don't know how to do that.
In short, I need a timer/counter that updates every second.
#interface className
{
NSTimer * timer;
}
#property (nonatomic, retain) NSTimer * timer;
#end
#implementation className
#synthesize timer;
...
/*factory method was corrected here. should work without warnings by copying and pasting */
-(void) applicationDidFinishLaunching : (UIApplication *) application {
timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(targetMethod:) userInfo:nil repeats: YES];
}
//define the target method
/*method was corrected because it needed parentheses around NSTimer */
-(void) targetMethod: (NSTimer *) theTimer {
NSLog(#"Me is here at 1 minute delay");
}
..
#end
taken from here
http://www.iphonedevsdk.com/forum/iphone-sdk-development/14403-nstimer-examples.html
If those first two lines are called immediately after each other, then it (should) always be less than a second. The date is being instantiated right there, and then the timeIntervalSinceNow is called immediately on it, when little/no time has occured between them. The goal is to make the date when first called, and then call the timeIntervalSinceNow on that to get times more than 0. However, this still has no creation of a updating timer as you want.
You could simply use an NSTimer to call a selector within your class at the required two second interval.
That said, you could possibly also make use of a CABasicAnimation to fade the opacity of the UIImageView, pending on the effect you require.