I've implemented a repeated task on button (UIButton) hold with the code from another Stack Overflow thread ( UIButton Touch and Hold ).
For various reasons, I'd like a repeat delay. definition of repeat delay
I can't quite wrap my head around how to do this however. I am relatively new to coding for the iPhone.
Inspired in the code you suggested you can go for something like this:
Make an NSTimer that will start up when the button is pressed and fire a method every x seconds.
Header (.h):
// Declare the timer and the needed IBActions in interface.
#interface className {
NSTimer * timer;
}
-(IBAction)theTouchDown(id)sender;
-(IBAction)theTouchUpInside(id)sender;
-(IBAction)theTouchUpOutside(id)sender;
// Give the timer properties.
#property (nonatomic, retain) NSTimer * timer;
Implementation file (.m):
// Synthesize the timer
// ...
Make an IBAction for "Touch Down" to start the timer that will fire the action method every 2 seconds. Then make another IBAction for "Touch Up Inside" and "Touch Up Outside" to invalidate the timer.
For example:
-(IBAction)theTouchDown {
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(action:)
userInfo:nil
repeats:NO];
}
-(IBAction)theTouchUpInside {
[self.timer invalidate];
self.timer = nil;
}
-(IBAction)theTouchUpOutside {
[self.timer invalidate];
self.timer = nil;
}
Then in that method fired by the NSTimer do whatever you need:
-(void)action:(id)sender {
// ...
}
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Blinking effect on UILabel
How to make a UILabel blink in iPhone. I have tried googling and in SO also. But those questions are animating the label to change the colour. But I dont need to change the colour. I jus need the UILabel to blink. Is it possible?
Here's the code snippet :
In your controller create a NSTimer property :
#property (strong, nonatomic) NSTimer *timer;
In your viewDidLoad method, start the timer :
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(toggleLabelAlpha) userInfo:nil repeats:YES];
Finally write the function to toggle the label
- (void)toggleLabelAlpha {
[self.label setHidden:(!self.label.hidden)];
}
Have fun
1: Create a NSTimer Object in your Class
NSTimer *updateTimer
2: Instantiate the Timer -
updateTimer = [NSTimer scheduledTimerWithTimeInterval:1000
target:self
selector:#selector(appUpdate:)
userInfo:nil
repeats:YES];
3: Write a Selector (method) that the timer uses -
- (void)appUpdate:(NSTimer*)theTimer
{
//Toggle the Text between a empty string and the text you want to display here
}
Following is my sample code.
#interface TrackTimer : NSObject {
NSTimer *timer;
}
#property (nonatomic, retain) NSTimer *timer;
- (void) startTimer;
- (void) stopTimer;
- (void) timerFired;
#end
TrackTimer.m
#synthesize timer;
- (void) startTimer
{
NSLog(#"Timer started ...");
if(timer)
{
timer = nil;
}
timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO];
}
- (void) stopTimer
{
NSLog(#"Timer stoped ...");
[tTimer invalidate];
}
- (void) timerFired
{
NSLog(#"Timer Fired ... :)");
}
I have to use the same timer object from 3 different view controllers, my problem is startTimer method do not invoke timerFired method in 2nd UIViewController. Its works perfectly on 1st and 3rd View Controller.
appln Flow : 1stView -> 2ndView -> 3rdView
You are doing everything right... almost.
Your timer does not fire, because of the "if" statement.
if (timer) {
timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(boom) userInfo:nil repeats:NO];
}
Here, the "if" statement returns NO, because the timer is not yet initialized..
The fact that you make it a property and synthesize it does not mean that (timer != nil)
If you remove the "if" statement it should work...
From the Apple docs on NSTimer:
The message to send to target when the timer fires. The selector must have the following signature:
- (void)timerFireMethod:(NSTimer*)theTimer
So, it looks like the signature of your timerFired method needs to be expanded to include one parameter '(NSTimer*)theTimer' and your selector needs to be #selector(timerFired:)
Don't really know how you do that, but NStimer has a class method called
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats. So you can do it like this:
timer=[NSTimer scheduledTimerWithTimeInterval:2 target:self
selector:#selector(timerFired)
userInfo:nil
repeats:YES];
This will invoke the timerFired method for you.
P.S.Here's the link to a simple app that does just what you want.
http://www.mediafire.com/?8uz115drqzb2nan
How can i start an NSTimer when a user clicks a button?
EDIT:i had the code working and everything, but i put the code to hide the button above the NSTimer code, and when i placed it below the NSTimer code it worked!
The line of code to start a timer is (read the docs here) :
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerFired:)
userInfo:nil
repeats:nil];
This will call the method timerFired: after 1 second i.e.
- (void)timerFired:(NSTimer *)timer {
NSLog(#"timer : %# has gone off", timer);
}
To get this to trigger from a button, you need this method in your .h file :
- (IBAction)buttonPressed:(id)sender;
and in your .m file, detect the button press like this :
- (IBAction)buttonPressed:(id)sender {
NSLog(#"Button's been pressed!");
}
Then, in interface builder connect the button's 'Toiuch Up Inside' action to this method.
Schedule it in the IBAction of the button. Check NSTimer Class Reference to know how to use timers.
You have to insert the command of[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerFired:)
userInfo:nil
repeats:nil];
inside the buttonclick. Then only you could make the NStimer run after the button click (if you want to run the timer after the button click event )
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.
I'm still new to programming so excuse me if this is silly. I'm programming a simple game and require multiple timers to send different messages at different intervals, so when creating the game, the following is called:
[self gameTimerValidate];
[self scoreTimerValidate];
- (void) gameTimerValidate
{
gameTimer = [NSTimer scheduledTimerWithTimeInterval:[myGame gIntervalSpeed] target:self selector:#selector(gameTimerInterval:) userInfo:nil repeats:YES];
}
- (void) scoreTimerValidate
{
scoreTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(scoreTimerInterval:) userInfo:nil repeats:YES];
}
I have the scoreTimer and gameTimer declared in my header file ("NSTimer *gameTimer;"). I invalidate the timers when pausing the game or completing the level, and call the above methods again when resuming the game or entering the next level, respectively.
I spent hours today trying to figure out why pausing the game would crash the application. After doing some debugging I noticed the retain count of gametimer was 0, and for scoretimer it was 2. Of course, I can't invalidate a timer with a retain count of 0, but I'm not sure how that came about.
Is there a specific way I must initialize two different NStimers? I been searching for hours on this to no avail...
NSTimer is a tricky class. It doesn't behave like you expect it to.
Firstly, the timer instances are not finally retained by the objects that initialize them but by IIRC, the NSRunLoop. This means that if you have an object that creates a timer, the timer will continue to be active even if you destroy the object that created it and all other references in your custom code. The timer will keep going along firing off messages and you have no clue where they're coming from.
Secondly, you can't stop/pause and resume a timer. When you invalidate it, it's dead.
I suggest creating a light class that will manage the timers for you so you don't have to keep track of it in the rest of your code. e.g.
#interface SDL_SimpleTimerController : NSObject {
NSTimer *currentTimer;
NSTimeInterval theInterval;
id theTargetObj;
SEL theSelector;
BOOL timerIsRunning;
}
#property (nonatomic,retain) NSTimer *currentTimer;
#property NSTimeInterval theInterval;
#property (nonatomic,retain) id theTargetObj;
#property SEL theSelector;
#property BOOL timerIsRunning;
-(SDL_SimpleTimerController *) initWithInterval:(NSTimeInterval)anInterval forTarget:(id)aTargetObj andSelector:(SEL)aSelector;
-(void) startTimer;
-(void) stopTimer;
#end
#implementation SDL_SimpleTimerController
#synthesize currentTimer;
#synthesize theInterval;
#synthesize theTargetObj;
#synthesize theSelector;
#synthesize timerIsRunning;
-(SDL_SimpleTimerController *) initWithInterval:(NSTimeInterval) anInterval forTarget:(id) aTargetObj andSelector:(SEL) aSelector
{
self=[super init];
theInterval=anInterval;
theTargetObj=aTargetObj;
theSelector=aSelector;
timerIsRunning=NO;
return self;
}// end initWithInterval:
-(void) startTimer{
if (currentTimer) {
currentTimer=Nil;
}
currentTimer=[NSTimer scheduledTimerWithTimeInterval:theInterval target:theTargetObj selector:theSelector userInfo:Nil repeats:YES];
timerIsRunning=YES;
}//end startTimer
-(void) stopTimer{
if (currentTimer) {
[currentTimer invalidate];
currentTimer=Nil;
}
timerIsRunning=NO;
}// end stopTimer
- (void)dealloc {
if (currentTimer) {
[currentTimer release];
currentTimer=Nil;
}
[theTargetObj release];
theTargetObj=Nil;
[super dealloc];
}
The timers are not reusable. After you invalidate them they are removed from the run loop and their retain count is decremented, resulting in their deallocation the next time through the loop. You'll either have to create new ones or stop invalidating them.
I think you should try to find where you might be doing a [scoreTimer retain], and where you might be invalidating (or releasing) gameTimer more than once (you only need to do the latter, if where you checked the retainCount, was after you had invalidated once). You can't increase the retainCount by calling either of
[self gameTimerValidate];
[self scoreTimerValidate];
more than once. You would leak memory, and have two timers firing at the same interval, but you wouldn't have one of those timers have a higher retainCount because of that.
If those two instance variables were retained properties, and you were setting them using self.gameTimer = ..., then I can see them getting retained an extra time. But the code I see doesn't explain your problem.
Search all instances of those two timers and see what else might be messing with things.
One suggestion, you might want to check for nil, like this:
- (void) gameTimerValidate
{
if (gameTimer == nil)
gameTimer = [NSTimer scheduledTimerWithTimeInterval:[myGame gIntervalSpeed] target:self selector:#selector(gameTimerInterval:) userInfo:nil repeats:YES];
}
- (void) scoreTimerValidate
{
if (scoreTimer == nil)
scoreTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(scoreTimerInterval:) userInfo:nil repeats:YES];
}
- (void) invalidateMyTimers {
[gameTimer invalidate], gameTimer = nil;
[scoreTimer invalidate], scoreTimer = nil;
}
Thanks for the replies, after giving it some thought I'm going to go with an approach similar to what TechZen said, and and just keep the timers running with a BOOL variable, and using that variable for checking events like pause and such (ie changing the boolean vs stopping and starting the timers).
(also my first time using this website, still learning the format of where the answers go) thanks again!
In reply to TechZen's light class, I think you should not release the target object above as you did not create it (therefore don't own it)
(void)dealloc {
if (currentTimer) {
[currentTimer release];
currentTimer=Nil;
}
**[theTargetObj release];**
theTargetObj=Nil;
[super dealloc];
}