NSTimer in Objective C when Click on Home Button - iphone

I have NSTimer in ViewController Class.. And all the methods for NStimer are in that class.. My NSTimer is working properly for play and pause... When i press home button on iPhone also the timer is working properly(it starts running from the time where the application enters into background when the application enters into foreground)... In my application i am rising an alert quickly when my application enters into foreground(UIAlertview in application didEnterForeGround). Here my NSTimer is running when the alert is on the screen(didn't give response to alert).. I want stop the NSTimer Upto the User responding to my alert... After that I want to start the NSTimer... How Can i do that...? Please help me... Thanks in Advance... I want call the NSTimer methods from Appdelegate.m file... Iam calling these methods properly... And the methods in ViewController are called.. But the action is Not Performing... For the Same method when call from viewController class it is working...
enter code here
ViewController.h
+(ExamViewController *)evcInstance;
ViewController.m
ExamViewController *evc = nil;
#implementation ExamViewController
+(ExamViewController *)evcInstance
{
if(!evc)
{
evc = [[ExamViewController alloc] init];
}
return evc;
}
- (void)onStartPressed
{
stopwatchtimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimer:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:stopwatchtimer forMode:NSRunLoopCommonModes];
resume.hidden = YES;
resume.enabled=NO;
pause.hidden = NO;
pause.enabled=YES;
}
- (void)onStopPressed
{
[stopwatchtimer invalidate];
stopwatchtimer = nil;
pause.hidden = YES;
pause.enabled=NO;
resume.hidden = NO;
resume.enabled=YES;
}
Appdelegate.m
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[ExamViewController evcInstance] onStopPressed];
NSLog(#"applicationWillEnterForeground");
if(viewCalled ==YES)
{
UIAlertView *alertview=[[UIAlertView alloc]initWithTitle:#"" message:#"DO! You Want To continue" delegate:self cancelButtonTitle:#"YES" otherButtonTitles:#"NO", nil];
[alertview show];
[alertview release];
}
/*
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
*/
}

a few things:
1) It looks like an incomplete singleton design. If you really want a singleton, refer to a post like this one.
2) No need to add stopwatchtimer to the current run loop. The scheduledTimerWithTimeInterval method schedules it for you.
3) I don't see where you declare stopwatch timer, but be sure to declare it as a retained property (or strong in ARC).
4) To stop the timer, just call [stopwatchtimer invalidate]; To restart it, re-instantiate and overwrite the old one using the same scheduledTimerWithTimeInterval class method that you called originally.
5) To do anything when an alert view completes, implement the delegate method:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
So you can invalidate the timer before presenting the alert, then rebuild it when you get notified the alert is finished.

Related

Updating UIView after some time interval?

I want to create Custom Views that call webservice after some time and update themself..The data should be updated even when the application is not in active state.. so What is the best way for doing this ??
Use NSTimer, but data will not update when application is in background mode. After application became active NSTimer will continue working.
you can use NSTimer for that. Add your code in your custom method and call that method like bellow..
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:#selector(yourMethodName:) userInfo:nil repeats:YES];
Here in scheduledTimerWithTimeInterval Parameter you can set the time in seconds so your method which passes in selector Parameter is called after every 3 seconds..
See one Example and tutorial of NSTimer From this link nstimer-tutorial-creating-clockstopwatchtimer-iphoneipad
UPDATE:
If you want to call webservice then you can use NSThread like bellow...
- (void) runTimer
{
[NSThread detachNewThreadSelector:#selector(updateAllVisibleElements)toTarget:self withObject:nil];
}
- (void) updateAllVisibleElements {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if(service == nil)
{
service = [[WebService alloc] init];
}
[service getlocationID:currentLatitude andlongitude:currentLongitute];
[pool release];
}
See the link Here
You'll be able to track the location, but you'll not be able to connect to your web-services when the app is in the background.

NSTimer scheduledTimerWithTimeInterval: repeats:NO selector method calls multiple times

I am using NSThread along with NSTimer.
My code is like this
-(void) checkForRecentAlarm
{
if ([self.alarmThread isFinished])
{
[self.alarmThread cancel];
}
self.alarmThread = [[NSThread alloc] initWithTarget:self selector:#selector(startTimerForRecentAlarm) object:nil];
[self.alarmThread start];
//[NSThread detachNewThreadSelector:#selector(startTimerForRecentAlarm) toTarget:self withObject:nil];
}
-(void)startTimerForRecentAlarm
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
self.recentAlarmTime = [NSDate date];
self.dbObject = [[RADataBaseModelManager alloc] init];
self.recentAlarmTime = [self.dbObject getMostRecentAlarmTimeFromDB];
if (self.recentAlarmTime) {
NSTimeInterval timeIntervalToAlarm = [self.recentAlarmTime timeIntervalSinceNow];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
//Fire timer every second to updated countdown and date/time
self.RATimer = [NSTimer scheduledTimerWithTimeInterval:timeIntervalToAlarm target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:NO];
[runLoop run];
}
[pool release];
}
- (void)timerFireMethod:(NSTimer*)theTimer
{
[self.RATimer invalidate];
[theTimer invalidate];
self.RATimer = NULL;
theTimer = NULL;
[self playAlarm];
UIAlertView *alarmAlert = [[UIAlertView alloc] initWithTitle:#"Alarm" message:#"" delegate:self cancelButtonTitle:#"Close" otherButtonTitles:#"Snooze", nil];
[alarmAlert show];
[alarmAlert release];
alarmAlert = nil;
}
Now the problem is, my alertbox comes twice for one call in the
startTimerForRecentAlarm
method. So that the alert comes consequently twice and my view get stuck.
What will be problem here?
I am trying to implement an alarm with multiple alarm option using a single NSTimer.
Please help.
When I debug this, I can find many simultaneous threads are running on same code(UIAlertView).
I can't see any obvious reason why that would be called twice, but it does seem like an overly complex way of doing what you need to do.
Have you thought about using local notifications?
If you don't want to do that, you could refactor your code so it works like this:
1. Add a new event
2. If there's no timer or the time to the event is shorter than the time on the timer, then set the timer for this event.
3. When a timer fires, check for the next event and set a timer for that event (if there is one).
This does seem really complex. My general observation is that if you get two timer firings it's because you have two timers for some reason.
If you have multiple threads doing UIAlertView, you have another problem, because you can only (reliably) do UI from the main thread.

switching from view 1 to view 2 without pressing any buttons

How to switch from view1 to view 2 without having any button pressed. In my views i have is uiimageview and uitextview
With the NSTimer i m trying to do this
in the viewdidload method by using the following code:
In the firstviewcontroller.h file
#interface FirstViewController : UIViewController
{
NSTimer *SwitchingTimer;
}
In the firstviewcontroller.m file
- (void)viewDidLoad
{
[super viewDidLoad];
SwitchingTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(SwitchView) userInfo:nil repeats:YES];
}
-(void)SwitchViews:(id)sender
In the secondviewcontroller.m file
-(void) SwitchView
{
SecondViewController *SecondView = [[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
SecondView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:SecondView animated:YES];
[SwitchingTimer invalidate];
self.SwitchingTimer = nil;
}
but nothing is happening. Can someone please tell me what i m missing in my code. Will appreciate help.
Thanks in advance.
There are a few issues in your code that are worth mentioning though I am not sure if those will provide you a solution.
Why do you want to repeat the timer every 2 seconds. I think you just want to switch to next view only once and if so then dont repeat the timer. So no need to invalidate the timer.
Your code for the SwitchView method is leaking memory. Please make sure that the SecondView is released after presenting the modal view(in case you are not using ARC).
Please follow the standard naming conventions. For eg: methods and variables should start with lowercase.
Regarding your issue please make sure that the nib name is correct and you are getting a valid object for the second view controller. You can check by using NSLog. Also ensure that the method Switchview is called. Try putting a break point and verify that it is called.
Another Option
If you just want to switch the view only once you can go for another option which does not make use of the NSTimer. For this, you can use performSelector:withObject:afterDelay:. This is just another option for the scenario I mentioned above.
You need to add it to the run loop:
- (void)viewDidLoad
{
[super viewDidLoad];
SwitchingTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(SwitchView) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer: SwitchingTimer forMode:NSDefaultRunLoopMode];
}
Also, you could rename -(void)SwitchView to -(void)switchViewTimerDidFire:(NSTimer *)timer. From the Documentation:
[...] The selector must have the following signature:
- (void)timerFireMethod:(NSTimer*)theTimer
The timer passes itself as the argument to this method.

How to cancel a previously scheduled event in Objective-C?

I am making an iphone app and have scheduled an event called gameOver to occur after 15 seconds(when the game is over).
[self performSelector:#selector(gameOver) withObject:nil afterDelay:15.0];
How can I unschedule this event so that if someone wanted to press a reset button and start the game over, this scheduled event would be deleted and another one would be created.
I would use an NSTimer
with the retain-property NSTimer *myTimer
-(void) startTimer
{
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:myDelay target:self selector:#selector(myTimerExpiredMethod:) userInfo:nil repeats:NO];
}
-(void) myTimerExpiredMethod:(id)aSender
{
NSLog(#"timer expired");
self.myTimer = nil;
}
-(void) cancelTimer
{
[self.myTimer invalidate];
self.myTimer = nil;
}
Try
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget
aTarget would be the object the selector should be performed on. self in your example.
Edit:
This method is defined on NSObject so:
[NSObject cancelPreviousPerformRequestsWithTarget:myObject]
would be the syntax.
Edit 2:
Use
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument
to only unschedule calls to this specific selector.

NSTimer as a timeout mechanism

I'm pretty sure this is really simple, and I'm just missing something obvious. I have an app that needs to download data from a web service for display in a UITableView, and I want to display a UIAlertView if the operation takes more than X seconds to complete. So this is what I've got (simplified for brevity):
MyViewController.h
#interface MyViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource> {
NSTimer *timer;
}
#property (nonatomic, retain) NSTimer *timer;
MyViewController.m
#implementation MyViewController
#synthesize timer;
- (void)viewDidLoad {
timer = [NSTimer scheduledTimerWithTimeInterval:20
target:self
selector:#selector(initializationTimedOut:)
userInfo:nil
repeats:NO];
[self doSomethingThatTakesALongTime];
[timer invalidate];
}
- (void)doSomethingThatTakesALongTime {
sleep(30); // for testing only
// web service calls etc. go here
}
- (void)initializationTimedOut:(NSTimer *)theTimer {
// show the alert view
}
My problem is that I'm expecting the [self doSomethingThatTakesALongTime] call to block while the timer keeps counting, and I'm thinking that if it finishes before the timer is done counting down, it will return control of the thread to viewDidLoad where [timer invalidate] will proceed to cancel the timer. Obviously my understanding of how timers/threads work is flawed here because the way the code is written, the timer never goes off. However, if I remove the [timer invalidate], it does.
I think there is a problem with scheduling a timer and doing a blocking call on the same thread. Until the blocking call is completed, the run-loop cannot fire the timer.
I suggest you to detach a thread to perform the long operation. Once the long operation is finished, call back on the main thread to invalidate the timer.
Note: it is important to invalidate the timer on the same thread it was scheduled.
- (void)viewDidLoad {
timer = [NSTimer scheduledTimerWithTimeInterval:20
target:self
selector:#selector(initializationTimedOut:)
userInfo:nil
repeats:NO];
[NSThread detachNewThreadSelector:#selector(doSomethingThatTakesALongTime:) toTarget:self withObject:nil];
}
- (void)doSomethingThatTakesALongTime:(id)arg {
sleep(30); // for testing only
// web service calls etc. go here
[self performSelectorOnMainThread:#selector(invalidate) withObject:nil waitUntilDone:NO];
}
- (void)invalidate {
[timer invalidate];
}
- (void)initializationTimedOut:(NSTimer *)theTimer {
// show the alert view
}
Have you tried to use [NSThread sleepforTimeInterval:30]; ?
The sleep() occurs on the main thread and the associated run loop never has the chance to invoke the selector for the timer.
If you would do real work in -doSomething that doesn't block the thread, e.g. non-blocking calls to web-services, it would work as expected. Blocking calls however would have to be done in a different thread so the main run loop does not get blocked.