I am trying to get a button to create a NSTimer which in turn will call a function (refreshView) to refresh UI elements, but I am having problems and I am not sure where the problem lies. Is the method signature wrong? Or am I getting the NSRunLoop part wrong? Or is it just horribly off base? Any help is appreciated.
-(IBAction)reload:(id)sender{
NSInvocation *displayInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:#selector(refreshView)]];
[displayInvocation setTarget:self];
NSTimer *slideShowTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
invocation:displayInvocation
repeats:YES];
[slideShowTimer fire];
NSRunLoop * a = [NSRunLoop currentRunLoop];
[a addTimer:slideShowTimer forMode:NSRunLoopCommonModes];}
-(void)refreshView{
[slideshow1 displayWithView:MajorImageView topicLabel:TopicLabel];
}
Your code seems very complicated for nothing. Do you want to (1) start a timer to call [refreshView] periodically, or (2) call it later.
For (1), simply setup a timer with,
[NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
target:(id)target
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats]
No need to use a method invocation, a target/action will be enough
For (2), if you want to call it later,
[NSObject performSelector:(SEL)aSelector
withObject:(id)anArgument
afterDelay:(NSTimeInterval)delay]
Related
This is what I want to do:
I want a timer, to fire a method and then, in the end of this method, be toggled off, and turn on an other timer on another method, and then entering a loop.
So what are the codes used to toggle between on and off the timer on a method?
In Delphi I use:
timer.enable:=True; // timer.enable:=False;
Are there a similar way to do it on objective-c?
I'm using Xcode 4.4
Thanks!
To turn the timer off, call invalidate on your timer like so:
[yourTimer invalidate]
And then to start a new one:
NSTimer *newTimer;
newTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 //Every how many seconds
target:self
selector:#selector(methodToCall)
userInfo:nil
repeats:YES];
Assuming your NSTimer is called "timer", you can use...
[timer invalidate]
to stop the timer. To make a timer pass a message to it's target method instantly, use
[timer fire]
To start a timer, you use one of the constructor methods listed in the documentation (https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nstimer_Class/Reference/NSTimer.html) such as
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(doThisWhenTimerFires:) userInfo:nil repeats:NO]
- (void)doThisWhenTimerFires:(NSTimer *)timer
{
//code here
}
Currently I am creating a NSTimer that calls my method every one second.
I do this like so:
[NSTimer scheduledTimerWithInterval:1 target:self selector:#selector(clocktick) userInfo:nil repeats:YES];
How can I stop this from calling clocktick?
I have tried assigning it like so: NSTimer *myTimer = [NSTimer ... and then using myTimer = nil; but that did nothing.
You need [myTimer invalidate] that will kill the timer for you.
You have to hold a reference to it, and then send the invalidate message to it:
[myTimer invalidate];
I hope it helps!
Have you tried using 'invalidate' ([timerInstance invalidate]; for example)?
Or is the problem that you don't have a reference to the timer instance anymore?
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimerDisplay) userInfo:nil repeats:YES];
[runLoop addTimer:self.timer forMode:NSRunLoopCommonModes];
This code-snippet is copied from my viewDidLoad method, so it is runned from the main-thread. All it do is to call a method to update a label.
I thought I need to have a own thread for doing this, but after getting help on this at SO I figured out that I did not.
However, I do not understand the NSRunLoopCommonModes. Why does it work?
AND the timer updates the label which is a "digital counter" which is on the same screen as a tableview so it CAN'T stop the timer even if the user holds the screen.
Thanks.
A NSRunLoop can run in different input modes. The mode defines which events are handled by the current runloop.
e.g.: If the current runloop is in event tracking mode, it only handles modal event loops. (e.g. dragging a NSScrollBar or a NSSlider on the Mac)
If you add your NSTimer only for NSDefaultRunLoopMode it won't fire if something is causing a modal event loop. (Details in Apple's documentation)
NSRunLoopCommonModes is an "alias" for multiple modes so that you don't have to do:
[[NSRunLoop currentRunLoop] addTimer:mRenderDurationTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:mRenderDurationTimer forMode:NSModalPanelRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:mRenderDurationTimer forMode:NSEventTrackingRunLoopMode];
I don't think you have to have this line at all, the first line is enough... I use PSYBlockTimer in my code which derives from the SDK method you use, but instead of a selector calls a block :
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:NO usingBlock:^ (NSTimer *t)
{
// stuff that will get executed in a second
}];
If you add your time to an instance of NSRunLoop under another thread, you need a while loop for this NSRunLoop of the thread. It looks like following:
do {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
} while (bDone);
Normally, I add the above code on my thread main function, and when thing is done, the thread go die and the autorelease pool of the thread will be released.
I have a task that runs periodically and it was originally designed to run on a separate run loop than the main runloop using NSThread and NSTimer.
What's the best way to adapt this to take advantage of GCD?
Current code:
-(void)initiateSomeTask
{
[NSThread detachNewThreadSelector:#selector(startTimerTask)
toTarget:self withObject:nil];
}
-(void)startTimerTask
{
// We won't get back the main runloop since we're on a new thread
NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];
NSPort *myPort = [NSMachPort port];
[myRunLoop addPort:myPort forMode:NSDefaultRunLoopMode];
NSTimer *myTimer = [NSTimer timerWithTimeInterval:10 /* seconds */
target:self selector:#selector(doMyTaskMethod)
userInfo:nil repeats:YES];
[myRunLoop addTimer:myTimer forMode:NSRunLoopCommonModes];
[myRunLoop run];
}
Is there anything I can do besides replace detachNewThreadSelector with dispatch_async?
You can replace the use of NSTimer with use of dispatch_source_create with DISPATCH_SOURCE_TYPE_TIMER. You won't need a run loop then.
Back in the original case, though, you don't really need to make a thread or use dispatch to run a timer. Kind of the point of run loops is that you don't need to make a thread to do something simple like a timer.
I've been using an NSTimer successfully, but am now having trouble with it. Undoubtably something stupid. Appreciate another set of eyes. Running the debugger, I see that applicationDidFinishLaunching is called, but trigger is never called.
-(void) trigger:(NSTimer *) theTimer{
NSLog(#"timer fired");
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
nst = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(trigger) userInfo:nil repeats:YES];
[window makeKeyAndVisible];
}
The selector must have the following signature:
- (void)timerFireMethod:(NSTimer*)theTimer
so you need
#selector(trigger:)
--edit--
Maybe you are doing this somewhere else, but in the code you included you do not actually start the timer. You have to add it to a NSRunLoop before it can trigger any events at all.
[[NSRunLoop currentRunLoop] addTimer:nst forMode:NSDefaultRunLoopMode];
If I read the examples correctly. I've only used the one the init method that automatically adds it to the current NSRunLoop. You really should look at the developer docs that someone included in the comments to my post.
Two things:
1) as others say, the method should have the following signature..
-(void) trigger:(NSTimer *) theTimer;
and you make the timer thus:
nst = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(trigger:) userInfo:nil repeats:YES];
2) merely creating the timer does not run it. As the documentation says:
You must add the new timer to a run
loop, using addTimer:forMode:. Then,
after seconds have elapsed, the timer
fires, invoking invocation. (If the
timer is configured to repeat, there
is no need to subsequently re-add the
timer to the run loop.)
Here's a piece of real functioning code that you can model after. The timer creation is the same as yours, but it also adds it to runloop the right way.
[[NSRunLoop currentRunLoop] addTimer:
[NSTimer timerWithTimeInterval:0.1
target:self
selector:#selector(someSelector:)
userInfo:nil
repeats:NO]
forMode:NSDefaultRunLoopMode];
The selector you're giving the timer, trigger, indicates that it should call a method that takes no parameter. Either change your timer-fired method to
- (void)trigger
{
// look at me, I don't take any parameters
NSLog(#"timer fired");
}
or change your initial timer call to use #selector(trigger:).
Your problem is due to the fact that timerWithTimeInterval:target:selector:userInfo:repeats: creates a timer but does not schedule it on the run loop, you have to do it yourself.
However, you may as well use this method which creates the timer and schedules it on the run loop: scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
I had a problem when starting timer in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { not in main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[self startScheduledTimer];
});