Objective C: constantly calling a function - objective-c

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.

Related

Action after progress bar loads

my progress bar is coded this way:
.h file
#interface ViewController : UIViewController {
IBOutlet UIProgressView *wait_progress;
double wait_time;
NSTimer *wait_timer;
}
-(void) increase_waiting_time;
.m file
-(void) increase_waiting_time {
wait_time = wait_time + 0.01;
wait_progress.progress = wait_time;
}
-(IBAction)start:(id)sender {
wait_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(increase_waiting_time) userInfo:nil repeats:YES];
}
How is it possible to do some action immediately after progress bar loads (that means wait_time will equals 1)?
A simple if statement will do the job. I guess you're falling foul of float number comparisons (testing for equality) which are problematic. Have a google on the subject. There are also answers on SO (like this one).
Basically, don't check for equality - it won't work. Instead, decide how accurate the comparison needs to be and test <= and >= to verify that the value is in that range. For you it may be good enough to simply use >= 1.

iOS - Execute a Piece of Code for 10 Seconds

I'd like to execute a piece of code every 500ms for a total of 10 seconds. I have the code working executing every half a second with this code
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(getLevel:) userInfo:nil repeats: YES];
but I cannot seem to figure out how to stop it executing after 10 seconds. The "repeats" argument doesnt seem to allow for a specified time.
Could someone point me in the direction of where I should go with this?
Thanks
Brian
You need to message invalidate on the NSTimer that scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: gives you. For example assign the NSTimer that method returns to you in a property or iVar of your class and after 10 seconds call invalidate on it.
Example:
.h:
#interface myClass{
NSTimer *myT;
}
[...]
.m:
[...]
{
myT = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(getLevel:) userInfo:nil repeats: YES];
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(stop) userInfo:nil repeats:NO];
}
-(void)stop{
[myT invalidate];
}
[...]
You could avoid another NSTimer if you want by implementing some kind of finish flag and track how many times you messaged the getLevel method, since it's every .5 seconds, that'll be 20 times.
But I would rather 2 NSTimer objects because you know it's 10 seconds regardless of the other timer, which you might decide to change, up or down it's frequency...
Another way you could go would be to define a selector, say -(void)stopTimer{}
in which you invalidate the timer. Then when you create the Timer in the first place do one of these:
[self performSelector:#selector(stopTimer) withObject:nil afterDelay:10.0];
The best thing to do is make a property on your class.
#property (nonatomic, strong) NSTimer *timer;
#property (nonatomic, strong) int ticks;
then in your getLevel selector call this:
if (ticks >= 20) [self.timer invalidate];
// 20 is 10 seconds * 500 milliseconds
ticks++;
You can set another timer to stop it using invalidate. Just keep a reference for this timer in an ivar or property.
Check this answer for more information.

NSRunLoop working OK on Simulator, Crashes iDevice

I'm working on an iOS App (FW: 5.0+ & ARC) which needs to update second by second.
Currently, I have this within a method (which is called in a performSelector when -ViewDidLoad):
-(void)FireOnload {
counter = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(countDownTime) userInfo:nil repeats:YES];
[counter fire];
[[NSRunLoop mainRunLoop] addTimer:counter forMode: NSDefaultRunLoopMode]; // <-- Causing Problem
}
-(void)countDownTime
{
double timeNow = [[NSDate date] timeIntervalSince1970];
double timeLeft = timeEnding - timeNow;
if(timeLeft < 60) // Less then 60 seconds, do something.
{
//Do Stuff
}
}
the counter variable is called in the header as NSTimer.
I explicitly call [counter fire] so it is called as soon as the view is loaded, not after 1 second.
This works fine in the iOS Simulator and will fire every second, but when it goes to the iDevice, it crashes.
I've commented out the NSRunLoop line, and the iDevice does not crash. However, it no longer updates every second.
What am I doing wrong here?
Found the Problem:
changed:
#property (weak, nonatomic) NSTimer *counter;
to:
#property (strong, nonatomic) NSTimer *counter;
If you're not using ARC, then you'll need to replace strong with retain
You say you use performSelector to invoke FireOnLoad. Why? If you are invoking it on a background thread, you need to know that NSRunLoop is not thread safe so you shouldn't access the main runloop except on the main thread.

Objective-C: Redrawing objects on screen

I've got a GameScreen.m file like (this is a simplified piece of the code):
- (IBAction) onCellClick:(id) sender
{
points +=1;
self.myScore.text = [[NSNumber numberWithInt: points] stringValue];
//myScore is a label in GameScreenViewController xib
}
That is, upon clicking a cell in the view, it will increase a text label by 1. So far so good.
then, in the same code, I've got a timer:
- (void) startTimer
{
[NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
}
its updateCounter method is:
- (void) updateCounter:(NSTimer *)theTimer
{
int seconds;
static int count = 0;
count +=1;
timeElapsed = [[NSString alloc] initWithFormat:#"%d", seconds + count];
self.time.text = timeElapsed;
//time is a label in GameScreenViewController xib
}
the thing is that "time" label is not updated (1 sec each time) in this case. I've inserted an AlertView to check if the startTimer method is valid and correctly called, and it actually is (it shows an annoying alertview each second with the timeElapsed value). However, I can' get the time label value to be changed.
Why is my score label updated upon action, while time label isn't updated every second? Is there any way I can update it without including my code in the ViewController?
//note: my coding splits into three files: the appDelegate flips screens and sends values among them; my viewControllers just the windows and, finally, my GameScreen class manages all the processes. From the xib, File's Owner is connected to the ViewController, and the view is connected to GameScreen class.
Thanks a lot for any feedback, please feel free to ask for any piece of additional code needed.
You have to do that (UI related operations) in main thread.
Instead of the line,
self.time.text = timeElapsed;
do as follows:
[self.time performSelectorOnMainThread:#selector(setText:) withObject:timeElapsed waitUntilDone:NO];
Edit:
- (void) updateCounter:(NSTimer *)theTimer
{
//int seconds;
static int count = 0;
count +=1;
NSString *timeElapsed1 = [[NSString alloc] initWithFormat:#"%d", count];
[self.time performSelectorOnMainThread:#selector(setText:) withObject:timeElapsed1 waitUntilDone:NO];
[timeElapsed1 release];
//time is a label in GameScreenViewController xib
}
I have gone through an UGLY walkaround. It works to some extent, but I went through such a crappy fix that I'm too embarassed to share...
Basically, I moved my timer directly to ViewController since I want it to be fired upon view load and can't get it to work with a call from ViewController's -(void)viewDidLoad to GameScreen's -(void) startTimer. All other stuff involving both methods is, pretty much, duplicated (ok, not duplicated, let's say 'polymorphed' since I handle some variables to fire them).
It seems my GameScreen.m IBActions can only fire other methods within my GameScreen.m, not on GameScreenViewController.m. Thus, I'm handling my buttons' behavior on GameScreen.m and, on GameScreenViewController.m, I just handle 'automatic' stuff; that is, anything not depending on user interaction. It made me have some IBOutlets duplicated depending on input/output needed so I guess that, since it's now working, you can't tell the difference if you don't go under the hood...
Thanks everyone for their feedback though.

Objective-C : NSTimer and countdown

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:).