Stop NSTimer when float is 0 - objective-c

I've got a NSTimer and a label which shows the seconds counting down.
-(void)start {
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
}
-(IBAction)stop {
[myTimer invalidate];
}
-(void)showActivity {
currentTime = [timeLabel.text floatValue];
currentTime -= 0.01;
timeLabel.text = [NSString stringWithFormat:#"%.2f", currentTime];
if (currentTime == 0) {
[self stop];
ResultViewController *screen = [[ResultViewController alloc] initWithNibName:nil bundle:nil];
screen.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:screen animated:YES];
[screen release];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
timeLabel.text = #"60.0";
[self start];
}
So when the time is 0, the timer should stop and the ResultViewController should load, but when I do it the timer still counts down into negative numbers and nothing happens.
Is there anybody who can help me?
Thank you :)

0.01 doesn't have an exact floating point binary representation, so your float will never get to exactly zero. Use <= instead of == in your comparison.

Try something like this:
if(currentTime <= 0.0)
...
What you're suffering from is floating point drift. It's a well known phenomenon.

Floating point arithmetic isn't "precise" oftentimes (see http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems ) . Change the check to if (currentTime <= 0.0) and you'll be fine.

Not all numbers can be stored accurately with floating point variables. Just as 1/3 can't be represented with finite digits in base 10, 1/10 can't be represented with finite digits in base 2. What you end up with is rounded to 0.10000000000000001 or close. So when you subtract 0.1 from that, you don't quite get 0.
The most robust solution to this is to store your time as milliseconds in an integer and divide it when you want to update the label. Don't bother going back and forth from the label to the number either. The canonical storage of the number should be as a number. The label is just to display it.
Edit:
For everybody recommending that he just change to <= instead - this is just a hack that barely scrapes by. Floating point representation of 0.1 is often 0.10000000000000001. What happens when you subtract 0.1 from that? It's still above 0. The conditional only triggers when it reaches almost -0.1, which isn't expected behaviour from the app. This is a bad solution.

ResultViewController should just alloc init instead of the alloc initWithNibName set to nil.
current time comparison should be put as if (currentTime <= (float)0.0) or similar (Reason for it refer to:this thread)

Better not compare against an int. Try to do this:
if (currentTime == 0.0) {

Related

How to Check Playback Time of Audioplayer increases by 2 seconds

I m trying to write a loop to check that whenever audio player.currenttime increases by 2 seconds then it should execute update view method
- (void)myTimerMethod{
NSLog(#"myTimerMethod is Called");
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(checkPlaybackTime:)
userInfo:nil
repeats:YES];
}
- (void)checkPlaybackTime:(NSTimer *)theTimer
{
float seconds = audioplayer.currenttime;
NSLog(#"Cur: %f",audioPlayer.currentTime );
if (seconds = seconds + 2){
[self update view];
}
- (void)UpdateView{
if (index < [textArray count])
{
self.textView.text = [self.textArray objectAtIndex:index];
self.imageView.image = [self.imagesArray objectAtIndex:index];
index++;
}else{
index = 0;
}
}
what is the correct way to write if audio player.currenttimer increases by 2 seconds then do this.
NSLog for current time always shows 0.00. Why is that. It should increase as the audioplayer is playing.
Thanks for help.
What i understood from your given explanation that you want to increment the time-interval something like this
Timer calls after 0.55
Timer calls after 0.60
Timer calls after 0.65
Timer calls after 0.70
& so on.
If that is what you are looking to do. Then i think you can do this way that by changing repeats:YES to repeats:NO so that the timer doesn't repeat, and then in onTimer, just start a new timer with a longer interval.
You need a variable to hold your interval so that you can make it a bit longer each time through onTimer.
Also, you probably don't need to retain the timer anymore, as it will only fire once, and when it does, you'll get a new timer.
float gap = 0.50;
[NSTimer scheduledTimerWithTimeInterval:gap target:self selector:#selector(onTimer) userInfo:nil repeats:NO];
-(void) onTimer {
gap = gap + .05;
[NSTimer scheduledTimerWithTimeInterval:gap target:self selector:#selector(onTimer) userInfo:nil repeats:NO];
}
Hope this helps you
First, try using your float "seconds" in your NSLog rather than the current time.
NSLog(#"Cur: %f", seconds);
current time is not a float, it's an NSTimer object so you would have to use %# in your NSLog text so
NSLog(#"Cur: %#",audioPlayer.currentTime );
Should work as well.
Assuming your audioPlayer is set up correctly, if you're looking for when the timer is at 2 seconds, your if statement will be
if(seconds == 2){
[self update view];
}
if you're looking for each time the timer hits an even number, i.e. 2, 4, 6, etc. your if statement will be
if(seconds % 2 == 0){
[self update view];
}
The % in an if statement is the modulo sign: http://www.cprogramming.com/tutorial/modulus.html
Also, your current if statement is assigning rather than checking the seconds variable. To check it, you need == not =. However, your current if statement will never be true since you're checking a variable by itself + 2. To put this another way, if seconds equals 2, your if statement is asking if 2 == (2+2) or if it it is 4, it's asking if 2 == (4+2). This statement cannot validate as true.
Hope this helps!

How many times a second should CADisplayLink's displayLink be called?

I have a CADisplayLink running in line with Chipmunk Physics, and I'm getting very slow performance. I put an NSLog in the method that's called on the CADisplayLink update, and it's being called an average of 22 times per second. I was under the impression that that should be nearer 60. I have the frameInterval set to 1, so should it be 60fps, in a perfect world? The delta times are averaging around 0.0167 seconds (and 1 / 60 IS 0.0167, which is confusing me even further).
I just have four walls around the bounds of my screen and just eight circle-shaped bodies on-screen, updating to UIButton instances on each call, so I don't think I'm doing anything that should tax it to this extent on both my 4S and iPad3. I'm applying a random force to each button once every 2.5 seconds in a separate method. Running in the simulator is butter-smooth, so it's a device-only issue. Can anyone help me spot what's causing the slowdown here, and what I can do about it?
Here's the relevant code, first that which sets up the link:
[NSTimer scheduledTimerWithTimeInterval: 2.5f target: self selector: #selector(updateForces) userInfo: nil repeats: YES];
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: #selector(update)];
_displayLink.frameInterval = 1;
[_displayLink addToRunLoop: [NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes];
Here's the method that should be called (I think!) 60 times per second, but is called only 22 or so:
if (!gameIsPaused) {
cpFloat dt = _displayLink.duration * _displayLink.frameInterval;
cpSpaceStep([[AGChipmunkSpace sharedInstance] space], dt);
for (LCBall *i in balls) {
cpVect pos1 = cpBodyGetPos(i.body);
CGAffineTransform trans1 = CGAffineTransformMakeTranslation(pos1.x, pos1.y);
CGAffineTransform rot1 = CGAffineTransformMakeRotation(cpBodyGetAngle(i.body));
i.button.transform = CGAffineTransformConcat(rot1, trans1);
}
}
And finally, here's the method that's called every 2.5 seconds, to apply the random forces (updateForces):
if (!gameIsPaused) {
for (LCBall *i in balls) {
int randomAngle = arc4random() % 360;
CGPoint point1 = [self getVectorFromAngle: randomAngle AndMagnitude: (arc4random() % 40) + ((arc4random() % 20) + 15)];
i.body -> f = cpv(point1.x, point1.y);
}
}
(Also, here's my method to get a vector from an angle, which I doubt is causing the issue):
angle = (angle / 180.0) * M_PI;
float x = magnitude * cos(angle);
float y = magnitude * sin(angle);
CGPoint point = CGPointMake(x, y);
return point;
Turns out I had a method on a different UIViewController in my storyboard that was firing every 0.1 seconds that hadn't turned off, and combined with the physics processing, was bogging things down.

Writing XML line every frame in 30FPS

Here is my code:
float timeInterval = 1 / [frameRateTextField floatValue];
recordingTimer = [NSTimer scheduledTimerWithTimeInterval:timeInterval
target:self
selector:#selector(recordingTimerSelector:)
userInfo:nil
repeats:YES];
- (void) recordingTimerSelector:(NSTimer*)timer{
NSXMLElement *timecode = [[NSXMLElement alloc] initWithName:#"timecode"];
[timecode setStringValue:[NSString stringWithFormat:#"%#,%#,%#,%#,%#",[DMXChannelArray objectAtIndex:0], [DMXChannelArray objectAtIndex:1], [DMXChannelArray objectAtIndex:2], [DMXChannelArray objectAtIndex:3], [DMXChannelArray objectAtIndex:4]]];
[root addChild:timecode];
time = time + 1;
[theRecordingTime setStringValue:[NSString stringWithFormat:#"%d", time]];
}
Is this the best way to go about doing this? I'm basically making a "recorder" with a resolution of 30FPS. Is there a way to make it go with the actual time, instead of being a separate entity? It might make it more accurate. Like:
10:40:41.0 - record element
10:40:41.3 - record element
10:40:41.6 - record element
Thanks!
You can get the current number of seconds since the reference date by asking NSDate for it.
Get that time interval when you start your timer, and then every time it fires, and subtract the starting time interval from the current one to get the number of seconds since you started.

When using a NSTimer in Objective-C, why isn't my code stopping when it gets to 0?

I am using a timer in xcode i have managed to make it count down correctly but when counting down it goes into negatives,
i have tried using an if statement to counter this but it dosnt seem to work here is the code i am using,
IBOutlet UILabel *timelabel;
int MainInt;
NSInteger fred;
NSTimer *timer;
MainInt -= 1;
timelabel.text = [NSString stringWithFormat:#"%i",MainInt ];
MainInt = 20;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countdown) userInfo:nil repeats:YES];
if (timelabel.text <= #"0")
{
[timer invalidate];
timelabel.text = #"20";
}
the H is ov my H
Although I believe it would compile
if (timelabel.text <= #"0")
This line doesn't make much sense. It should probably read:
if(MainInt <= 0)
Instead of trying to compare the text value of the label with zero, compare MainInt. Your problem is that you're trying to compare two strings... Technically, you can get the ASCII value of a character and compare it to another, so basically you tried to do something like #"Dog" >= #"Cat" which the runtime will attempt to do but obviously isn't what you want.
There's also a method on NSString called intValue that you could use to pull out an integer representation of the string. You could do
if ([timelabel.text intValue] <= 0)
If you really want to check the timelabel.text use the intValue
if ([timelabel.text intValue] <= 0)
{
timer invalidate];
timelabel.text = #"20";
}
Note: Change to intValue, but take note you should compare it to int also

Using two NSTimer

I'm creating this app that converts text to Morse code and then flash it out using the iPhone's flashlight. I have used string replacement to convert the content of a NSString to Morse code. I have found a script that turns the iPhone's flashlight on and off, with adjustable intervals using NSTimer. But I can't figure out how to add two different intervals, one for the morse "." and one for the morse "-". Can anyone help me?
- (void)viewDidLoad {
[super viewDidLoad];
int spaceTime;
spaceTime = 1;
int flashTimePrik;
flashTimePrik = 5;
strobeIsOn = NO;
strobeActivated = NO;
strobeFlashOn = NO;
flashController = [[FlashController alloc] init];
self.strobeTimer = [
NSTimer
scheduledTimerWithTimeInterval:spaceTime
target:self
selector:#selector(strobeTimerCallback:)
userInfo:nil
repeats:YES
];
self.strobeFlashTimer = [
NSTimer scheduledTimerWithTimeInterval:flashTimePrik
target:self
selector:#selector(strobeFlashTimerCallback:)
userInfo:nil
repeats:YES
];
}
- (void)strobeTimerCallback:(id)sender {
if (strobeActivated) {
strobeIsOn = !strobeIsOn;
strobeFlashOn = YES;
} else {
strobeFlashOn = NO;
}
}
- (void)strobeFlashTimerCallback:(id)sender {
if (strobeFlashOn) {
strobeFlashOn = !strobeFlashOn;
[self startStopStrobe:strobeIsOn];
} else {
[self startStopStrobe:NO];
}
}
Just use one timer, set the time interval based on the dot, dash or space interval. For "A" which is dot space dash
Turn on the light and set that timer to the dot interval.
When the timer fires, turn the light off and set the timer to the space interval.
When the timer fires, turn the light on and set the timer to the dash interval.
When the timer fires, turn the light off.
Use a bajillion timers. All single fire mode. Want an short call call to flash short. Then in the timer call back, create another timer for the next dash or dot. When there are no more signals to transmit in the array, you are done. Code is approximate....
- (void)lightTimerOffCallback:(id)sender {
turnLIGHTOFF
[NSTimer scheduledTimer:intervalbeforeStartingNextChar... selector(#startNextDotOrFlash) repeat NO]
}
- (void)startNextDotOrFlash:(id)sender {
if (there is a new dot or dash to do)
intervalToLeaveThisLightOn = 1.0 : 0.1 ? isDot;
turnLIGHTON
[NSTimer scheduledTimer:intervalToLeaveThisLightOn... selector(#lightTimerOffCallback) repeat NO]
}
Don't need the timer in an iVar.