I need some UIViewControllers that receive a NSNotification from the app delegate. It's like a timer, but every UIViewController handle your way. My problem is: when I interact with user interface, the UIViewController doesn't receive the notification, causing problems.
Here is my code in AppDelegate:
-(void)updateCounter:(NSTimer *)theTimer{
[[NSNotificationCenter defaultCenter] postNotificationName:TimeTickNotification object:nil];
}
//*called by some trigger in the app
-(void) startTimer{
timer = [NSTimer
scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
}
I am handling the notifications in each UIViewController like this:
-(void) updateGlobalTime:(NSNotification *) notification{
totalTime = [NSNumber numberWithFloat:([ficha.tempoTotal floatValue] + STEP)];
}
-(void) viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateGlobalTime:)
name:TimeTickNotification
object:nil];
}
What should I do to interact with UI and update it at same time? Maybe the NSNotification is not being thrown while user interacts with UI.
You need to make sure you're updating any UI on the main thread. If you want to update the UI to have the new totalTime in a label or something, make sure the setText: function is running on the main thread. You can accomplish that with GCD, like this:
-(void) updateGlobalTime:(NSNotification *) notification{
totalTime = [NSNumber numberWithFloat:([ficha.tempoTotal floatValue] + STEP)];
// Update label on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[label setText:totalTime];
});
}
The solution was to use NSRunLoop, as following:
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
timer = [NSTimer
scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode:UITrackingRunLoopMode];
Related
I am making an iOS application that uses wireless communication. One of its features is checking if the external devices that it is connected with are responding. So what I tried to do, is to make a "Device" class for every connected device, and then for each of them create a NSTimer that would handle the timeouts. And I made it like this:
The "Device" class init:
NSTimer* communicationChecker;
- (id)initWithAddress: (NSString*) address;
{
self = [super init];
if (self)
{
_address = address;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateStatus:) name:NOTIFICATION_STATUS object:nil];
communicationChecker = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(iAmDead:) userInfo:nil repeats:YES];
self.readyToRoll = true;
}
return self;
}
The timer selector:
- (IBAction)iAmDead:(NSTimer*)sender
{
self.readyToRoll = false;
NSLog(#"%# is dead :(", self.address);
}
And the notification selector:
-(void)updateStatus:(NSNotification *) notification
{
NSDictionary* userInfo = notification.userInfo;
NSString* deviceAddress = (NSString*)userInfo[PARAM_DEVICE_ADDRESS];
if ([_address isEqualToString:deviceAddress]) {
self.readyToRoll = true;
[communicationChecker invalidate];
communicationChecker = nil;
communicationChecker = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(iAmDead:) userInfo:nil repeats:YES];
}
}
So how I thought this would work, is that every time that a notification comes for that given device, it would change its "readyToRoll" variable and reset the timer. The problem is that only one device declares that it's dead (when none of them report status), and it's the one that sent the last status report message. I really have no clue how to go about this. What causes this behaviour?
I have solved the problem by moving the NSTimer declaration from the .m file, to .h. By adding the NSTimer as a property (#property NSTimer* communicationChecker;) it is initiated for each device. Everything works as expected now.
I think that the NSTimer was initiating only once earlier, and was only restarted with different parameters. Now each device has its own timer.
Now this might be total wrong, but did you try to initialize your timers like this :
NSTimer *timer = [NSTimer timerWithTimeInterval:kUpdateTimeInterval
target:self
selector:#selector(doStuff)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
That way you ensure that they are added to the mainRunLoop, and not the "currentLoop" as done in the scheduledTimerWithTimeInterval function.
Try and let me know.
I have gone through many sites but still no answer.
I have a method suppose void xyz(), which will get called automatically from a View Controller after every 3 seconds.
I have no idea what to use, do I have to use NSThread or PerformSelector.
Call this method from ViewDidLoad method.ViewDidLoad will when your view will be appear in iPhone device or Simulator.
[NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:#selector(runMethod) userInfo:nil repeats:YES];
-(void)runMethod
{
}
Something like this
-(void)xyz{
[self performSelectorInBackground:#selector(xyz) withObject:nil];
}
- (void)viewDidLoad {
[self performSelector:#selector(xyz) withObject:nil afterDelay:0.3];
}
Use NSTimer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:#selector(xyz) userInfo:nil repeats:YES];
You should use NSTimer as mentioned by #mokujin.
Please visit https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html
Hello guys I am in need.. Please help me out.
Below is the thread i run in my app which is called every .30delay.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
notification = [[NSNotificationCenter alloc] init];
notificationTimer = [NSTimer scheduledTimerWithTimeInterval:.30 target:self selector:#selector(notificationTimerFired:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
});
What i want is in method "notificationTimerFired" i am calling one more method and that is to be called say for 5sec interval.. How can i do that.. I tried to adding following code but at the 1st time it called for specified delay but later it calls continually with dispatch_async method is called. Please reply me I am badly in need
[NSTimer scheduledTimerWithTimeInterval:.30 target:self selector:#selector(notificationTimerFired:) userInfo:nil repeats:YES];
Folks, I found solution to my problem and it goes as follows
//run only once for specified delay
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
notification = [[NSNotificationCenter alloc] init];
notificationTimer = [NSTimer scheduledTimerWithTimeInterval:frequency*60
target:self
selector:#selector(repeateThreadForSpecificInterval:)
userInfo:nil
repeats:YES];
// Do any other initialisation stuff here
});
dispatch_once is the key which I was searching for. Any way thanks for your replies..
I totally understand if they are, but what I'm looking for is a timer that pauses when the application enters the background and unpauses after the user returns to the app. I do not need a background task; I just want to make sure that after approximately x minutes within the app a certain action occurs whether that is today or tomorrow.
Thanks!
Brett
Backgrounding the app (assuming you have no background task) doesn't "pause" the timer. It's still counting down in theory so if the app is reopened, it will fire if enough time has passed. This goes for NSTimer's as well. (Let me know if you want more details as to why and I'll edit the answer).
Consider using the following code:
#implementation MyCustomClass {
int elapsedTime;
NSTimer *timer;
}
- (id) init {
if ( ( self = [super init] ) ) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredForeground)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
return self;
}
- (void) applicationEnteredForeground {
timer = [NSTimer timerWithTimeInterval:1
target:self
selector:#selector(timerTicked)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void) applicationEnteredBackground {
[timer invalidate];
}
- (void) timerTicked {
elapsedTime += 1;
// If enough time passed, do something
}
i am using the following method in a uiview subclass:
[self performSelector:#selector(timeout) withObject:nil afterDelay:20];
The method is called after 20 seconds as expected.
In another method i try to cancel the perform request using the following code:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(timeout) object:nil];
i've also tried
[NSRunLoop cancelPreviousPerformRequestsWithTarget:self selector:#selector(timeout) object:nil];
both messages don't bring the expected result an the timeout method is still called.
can anybody explain me what i am doing wrong and how to do it the right way ?
cheers from austria
martin
Two points
1. Are both self same object??
2. Is [NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(timeout) object:nil]; performed on same thread on which you called [self performSelector:#selector(timeout) withObject:nil afterDelay:20]; ?
Check these two problems.
Use an NSTimer stored as an instance variable in your class. When you want to cancel the perform, invalidate and destroy the timer.
In your #interface:
#property (readwrite, retain) NSTimer *myTimer;
In your #implementation:
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:20 target:self selector:#selector(timeout) userInfo:nil repeats:NO];
Then, if some condition happens and the timeout method should no longer be called:
[self.myTimer invalidate];
self.myTimer = nil; // this releases the retained property implicitly
Try this:
[self performSelectorOnMainThread:#selector(timeout) withObject:self waitUntilDone:NO];
You can do that with 2 ways :
You could use this which would remove all queued
[NSObject cancelPreviousPerformRequestsWithTarget:self];
you can remove each one individually
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(timeout)
object:nil];