When I create a new timer with an IBAction and I double click the button, it creates two timers.
How can I write the code so there's only 1 timer and if I push the button it doesn't create a new timer?
Sorry for my bad English I'm 13 and from Germany.
Here is the code I use to create the timer:
- (IBAction)start:(id)sender;
{
progressBarUpdate = [NSTimer scheduledTimerWithTimeInterval:0.003 target:self selector:#selector(progressbarupdate) userInfo:nil repeats:YES];
//startet den timer
}
safe the timer as a member variable and check if already exists
#implementation MyViewController {
NSTimer *_timer;
}
...
- IBAction)start:(id)sender {
if(!_timer) {
_timer = [[NSTimer timerWith...] retain];//with arc, forget the retain
}
}
...
#end
Check progressBarUpdate.isValid before recreating the timer is an easy way to do this
Related
Hello I wanted to run a NSTimer on the main thread im not sure if they are run on the main thread by default or I have to do a special implementation ? thanks to anyone who could help
#interface RootViewController : UIViewController {
NSTimer *minutePassed;
}
- (void)adViewDidLoad
{
minutePassed = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:#selector(callMinutedPassed) userInfo:nil repeats:NO];
}
-(void)callMinutePassed
{
NSLog("Minute Passed");
}
Documentation for scheduledTimer(timeInterval:target:selector:userInfo:repeats) states Creates a timer and schedules it on the current run loop in the default mode.. That means in your instance, that it is running on the main thread. I'm assuming when you say -(void)adViewDidLoad you mean -(void)viewDidLoad().
I am trying to make a timer app. I am fine with Play button but I couldnt get Pause button working. I have seen some tutorials on Timer Apps and most of them have only used: [timer invalidate] code for that method that solely stops the time that is currently being shown in the label (display). Even that code doesn't work for me so I tried doing this which makes kinda more sense but still, of no luck.
#implementation ViewController
int timerCounter=0;
NSTimer *timer;
NSString *label;
BOOL isPaused=NO;
-(IB Action) playButton:(id)sender{
[timer invalidate];
isPaused=NO;
timer=[NSTimer scheduledTimerWithTimeInterval:1 target:self selector: #selector(tick) userInfo:nil repeats:YES];
}
-(IBAction) pauseButton:(id)sender{
[timer invalidate];
isPaused=YES;
label=[NSString stringWithFormat:#"%d",timerCounter];
_labelTimer.text=label;
}
-(void) tick{
if (isPaused==NO){
timerCounter++;
}
label=[NSString stringWithFormat:#"%d",timerCounter];
_labelTimer.text=label;
}
The NSTimer API do not have any method for pausing. What is available is either fire or invalidate. About your code, You are using global variables - not a good practice, most probably the instance of timer you are calling is not the same, remove them and add a property in the class extension in .m instead:
#property (nonatomic, strong) NSTimer * timer;
you then address that property with self.timer.
If this does not help, check if the button call the method when you press it.
My app adds a custom SKNode object to a SKScene at a fixed interval (sub second) using an NSTimer. At certain times I want the timer to speed up. Here's what I do (code simplified):
#interface MyScene : SKScene
#end
#implementation MyScene {
MyNode *node;
NSTimer *timer;
int speed;
}
- (id):initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
speed = 0.8f;
timer = [NSTimer = scheduledTimerWithTimeInterval:speed target:self selector:#selector(addNodeToScene) userInfo:nil repeats:YES];
}
}
- (id):addNodeToScene {
if (node != nil) {
[node removeFromParent];
node = nil;
}
CGPoint location = //random x y coordinates using arc4random()...
node = [[MyNode alloc] initAtLocation:location];
[self addChild:node];
}
// at some point I call this method (called regularly throughout life of app)
- (id):speedUp {
[timer invalidate];
timer = nil;
speed *= 0.9f;
timer = [NSTimer = scheduledTimerWithTimeInterval:speed target:self selector:#selector(addNodeToScene) userInfo:nil repeats:YES];
}
I've noticed a slight lag every time i call speedUp. I'm very new to Objective-C so not sure how to resolve. Is it more efficient to use dispatch queues over a timer or can I not avoid this issue because the internal is so fast?
I ran time profiling on instruments and this was not highlighted - instead my heaviest method was addNodeToScene.
My guess is that you're calling -speedUp outside of the normal work in -addNodeToScene. If so, the lag is likely coming from the fact that you've already used up a bit of the previous delay, then -speedUp invalidates the old timer and creates a new one, which starts the delay all over again. You'll see a lag any time you're more than 10% beyond the previous timer fire.
I'll try some ASCII art to illustrate. + is a timer fire, * is when you call -speedUp and it resets the timer.
+---------+--------+---------+---------+---------+
+---------+---*--------+--------+--------+--------+
Found the solution in another question: Xcode / Objective-C: Why is NSTimer sometimes slow/choppy?
(use CADisplayLink instead of NSTimer)
I don't want to create NSTimer object. How do I invalidate timer? I want to invalidate timer in viewWillDisappear.
-(void) viewDidLoad
{
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
}
A
you have to hold on to the timer you create:
#interface MONObject ()
#property (nonatomic, retain) NSTimer * timerIvar;
#end
#implementation MONObject
...
- (void)viewDidLoad
{
[super viewDidLoad];
self.timerIvar = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
}
- (void)invalidateTimer
{
[self.timerIvar invalidate];
self.timerIvar = nil;
}
- (void)viewWillDisappear:(BOOL)animated
{
...
[self invalidateTimer];
}
B
another option would be to invalidate the timer that is passed in the callback, but that won't occur within viewDidUnload:. therefore, it doesn't quite apply in this scenario:
- (void)onTimer:(NSTimer *)pTimer
{
[pTimer invalidate];
}
If you want to be able to cancel the timer, you have to refer to the timer you’re cancelling, and that means you have to keep the pointer to the timer around, see justin’s answer.
Keeping a reference to the timer is the right way to do it, but for the sake of completeness you may also use the -performSelector:withObject:afterDelay: method as a poor man’s timer. That call may be invalidated using +cancelPreviousPerformRequestsWithTarget:. Sample code:
- (void) viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(timerTick) withObject:nil afterDelay:10];
}
And then:
- (void) viewWillDisappear
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[super viewWillDisappear];
}
But this is not the right way to do it, because there might be other perform-selector requests pending on your object that you would cancel. It’s best to keep your timer around, that way you know exactly what you’re cancelling.
By the way, it’s also probably a bad idea to run a timer in -viewDidLoad. View loading may happen anytime, without any relation to view being displayed.
Maybe this method can help you:
[self performSelector:#selector(onTimer:) withObject:nil afterDelay:10];
If you don't want to hold on to your timer, the NSTimer object will be passed to the timer method (in your case onTimer:), so in that method you could check whether the timer is still needed and invalidate it. However, you will run into trouble if the view comes back before you invalidated the timer, and you create a new one.
By far the best way is to store the timer into an instance variable. It works, no clever tricks, and you'll know six months later what you did. I'd probably write a
#property (readwrite, nonatomic) BOOL hasTimer;
getter returns YES iff the timer is not nil, setter invalidates the timer or creates a new one.
I am a newbie IOS developer, but I have a good amount of experience in Android development. My question is regarding the creating and use of interval specific timers.
In android I could easily make a timer like this:
timedTimer = new Timer();
timedTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
TimedMethod();
}
}, 0, 1000);
Where the interval is 1000 MS and the method TimedMethod() is called on every tick. How would I go about implementing a similar function in IOS?
Thanks so much for reading! Any help at all would be great! :-)
You can use a repeating NSTimer like so:
- (void) startTimer {
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(tick:)
userInfo:nil
repeats:YES];
}
- (void) tick:(NSTimer *) timer {
//do something here..
}
Use
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerCallback) userInfo:nil repeats:YES];
In the same class as you called the above method, create a method called timerCallback. This will be called every time your timer fires; every 1000 milliseconds.
Use below method present in NSTimer.h file of Foundation Framework
Syntax :
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
Usage :
#define kSyncTimerLength 4 //Declare globally
-(void) timerActivityFunction; //Declare in interface
[NSTimer scheduledTimerWithTimeInterval:kSyncTimerLength target:self
selector:#selector(timerActivityFunction) userInfo:nil repeats:NO];
-(void) timerActivityFunction {
// perform timer task over-here
}
For Swift:
Create a timer object using below line which will call upload method every 10 second. If you get does not implement methodSignatureForSelector extend your class with NSObject. Read this for more information Object X of class Y does not implement methodSignatureForSelector in Swift
timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: "upload", userInfo: nil, repeats: true)
func upload() {
print("hi")
}