I'm trying to create a simple countdown timer app for myself. So far I've figured out how to create the countdown timers with a stop/reset action on them for a single button I've got attached to the timer.
However, I would like to add multiple timers to the same page and I'm not really sure how to do about making extra calls for the timers. Each of the timers would have it's own number to count down from (7 minutes for one, 3 minutes for the other, etc). These are set intervals that the user is not able to change. Google hasn't really worked out for me on how to do this so I'm hoping someone can at least guide me in the right direction. Below is my code snippets:
ViewController.h
#interface ViewController : UIViewController {
IBOutlet UILabel *firstCountdownLabel;
NSTimer *firstCountdownTimer;
bool timerActive;
int secondsCount;
}
- (IBAction)start:(id)sender;
- (void)timerRun;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (void) timerRun {
secondsCount = secondsCount - 1;
int minutes = secondsCount / 60;
int seconds = secondsCount - (minutes * 60);
NSString *timerOutput = [NSString stringWithFormat:#"%2d:%.2d", minutes, seconds];
firstCountdownLabel.text = timerOutput;
if (secondsCount == 0) {
[firstCountdownTimer invalidate];
firstCountdownTimer = nil;
}
}
//- (void) setTimer {
- (IBAction)start:(id)sender {
secondsCount = 420;
if (timerActive == NO) {
timerActive = YES;
self->firstCountdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerRun) userInfo:nil repeats:YES];
}
else {
timerActive=NO;
[self->firstCountdownTimer invalidate];
self->firstCountdownTimer = nil;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// [self setTimer];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Google doesn't help in showing you how to implement original application ideas.
If you want multiple timers, define multiple timer instance variables:
#interface ViewController : UIViewController
{
IBOutlet UILabel *timer1Label;
IBOutlet UILabel *timer2Label;
IBOutlet UILabel *timer3Label;
NSTimer *timer1;
NSTimer *timer2;
NSTimer *timer3;
int timer1Count;
int timer2Count;
int timer3Count;
bool timer1Active;
bool timer2Active;
bool timer3Active;
}
Then create a separate IBAction for each button that starts each of the timers:
- (IBAction)startTimer1:(id)sender
{
timer1Count = 420;
if (timer1Active == NO)
{
timer1Active = YES;
timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timer1Run:)
userInfo:nil
repeats:YES];
}
else
{
timer1Active=NO;
[timer1 invalidate];
timer1 = nil;
}
}
- (void) timer1Run: (NSTimer*) timer
{
timer1Count -= 1;
int minutes = timer1Count / 60;
int seconds = timer1Count - (minutes * 60);
NSString *timerOutput = [NSString stringWithFormat:#"%2d:%.2d", minutes, seconds];
timer1Label = timerOutput;
if (timer1Count == 0) {
[timer2 invalidate];
timer2 = nil;
}
}
Duplicate the above code for each timer, using "timer2" and "timer3" in place of "timer1". Change the time counts for each one to the desired values. (I changed the names from "firstTimer" to "timer1" because it's easier to edit the code to support multiple timers that way.
I did not write 3 versions of each method for you because you need to figure this out rather than copy & pasting in code that you don't understand.
It would be possible, and require less code, to use the same IBAction method for all your start timer buttons, and have the code check the tag on the button to decide which timer to start.
The code might look like this:
- (IBAction)startTimer1:(id)sender
{
int tag = [sender tag];
switch (tag)
{
case 1: //timer 1
//Put code to start timer 1 here
break;
case 2: //timer 2
//put code to start timer 2 here
break;
}
}
But that might be a bit over your head at the moment.
By the way, forget you ever saw the "self->variable" syntax. it is slower and more error-prone than just referring to the instance variable directly. using object->variable syntax also allows you to access the instance variables of other objects, which is bad practice. You should always use properties to access the instance variables of objects other than yourself.
Also, the timer method should take a single parameter, a timer. I corrected the timer method in the above code.
Create a class as YourTimer with few properties like
NSString *timerLabel;
NSTimer *timer;
NSInteger timerCounter;
Now create an array of YourTimer objects. Then you can access it easily.
This will be modular, maintainable and reusable code, as may be later on you need one more identifier to be with all timers, hence wrap them in one class and use it.
Related
I am making a game and I need to know if you can make a if statement that makes it like this:
if object.hidden = YES for 5 seconds{
do these things
}
Could someone please tell me if this is possible, and if so how this would work?
Put a timestamp on your object at the time it was hidden. Then convert your test into a "how long has this object been hidden". Pseudocode (since I don't actually have experience with objective C):
if hidden and elapsed time since hidden > 5 seconds:
do stuff
You can use CACurrentMediaTime(), for an accurate time interval.
sample:
CFTimeInterval start = CACurrentMediaTime();
for(int i=0; i<=10000; i++) {
NSLog(#"%i", i);
}
CFTimeInterval elapsed = CACurrentMediaTime() - start;
NSLog(#"elapsedTime : %f", elapsed);
Make sure you've added QuartzCore framework in your traget's settings
You could subclass Object and build that functionality in. Something like:
#interface ObjectSubclass : Object
#property (nonatomic, strong) NSTimer *timer;
#end
#implementation ObjectSubclass
-(void)setHidden:(BOOL)hidden{
[super setHidden:hidden];
if (hidden){
if (self.timer) {
[self.timer invalidate];
}
self.timer = [NSTimer timerWithTimeInterval:5
target:self
selector:#selector(doStuff:)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:self.timer
forMode:NSDefaultRunLoopMode];
} else {
[self.timer invalidate];
self.timer = nil;
}
}
I and many others have a issue mathematically causing a function to be called more and more often. My goal is to call the code inside the if statement more and more often. The function is called every .01 seconds. I would like the first time it runs is at 1 second, then faster and faster until it holds off at about .3. I need to know what to put in the SOMETHING.
The Function is called every .01 seconds by a NSTimer.
The code is:
-(IBAction)redmaker:(id)sender{
refreshrate = refreshrate+1;
if(SOMETHING){
int rand =arc4random() %65;
UIButton *button = (UIButton *)[self.view viewWithTag:rand];
button.backgroundColor = [UIColor colorWithRed:255 green:0 blue:0 alpha:1];
button.enabled = YES;
deathtimes[rand] = 10;
rate = rate+1;
refreshrate = 0;
}
You should use an NSTimer to call your method. Define an NSTimer in your header file.
Class.h
NSTimer *timer;
double interval;
Class.m
//put the following two lines in viewDidLoad, or some other method
interval = 1.0
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(redmarker:) userInfo:nil repeats:NO];
-----
//put this at the bottom of your if statement
if (interval > 0.3)
{
[timer invalidate];
//change this value to something greater to call the method faster
interval -= 0.05;
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(redmarker:) userInfo:nil repeats:NO];
}
You may experience an issue that causes your game to slow down. If that occurs, then it is possible that the main thread is unable to handle the timer, the buttons, and other actions all at the same time. You will want to look into using Grand Central Dispatch.
Repeating timers always use the same time interval. You can't change it.
If you want a timer on a decreasing interval, create a non-repating timer that triggers a new timer with the same selector each time it fires. Use an instance variable to hold the interval, and subtract some amount from the interval value each time it fires.
As for your "if (SOMETHING)", nobody else can tell you the conditions in your code that would decide what to do.
Can't you use Grand Central Dispatch with a recursive method like this:
#import "ViewController.h"
#interface ViewController ()
{
CGFloat fireTime;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
fireTime = 1.0;
// initial call to method
[self foo];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)foo
{
NSLog(#"Hello at, timer fired off after %lf", fireTime);
if (fireTime > 0.3)
{
// decrement timer
fireTime -= 0.1;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(fireTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self foo];
});
}
#end
I am trying to increment a NSNumber every 5 seconds by 1 in Cocos2d but for some reason it isn't working (probably obvious mistake). I am using the update method but I think this may be the wrong place. Currently it is only adding +1 once. I need it to do this every 5 seconds constantly:
-(void) update: (CCTime) delta
{
// Save a string:
NSNumber *anInt = [NSNumber numberWithInt:500];
NSNumber *bNumber = [NSNumber numberWithInt:[anInt intValue] + 1];
NSLog(#"Update Number%#",bNumber);
}
An easy way to run something every 5 seconds would be to:
Create a property storing your number and a timer:
#property (nonatomic, assign) int bNumber;
#property (nonatomic, strong) NSTimer* timer;
Then initialize the number to 500 (I assume based on your example you want it to start at 500) and create the timer:
- (instanceType)init
{
self = [super init];
if (self)
{
self.bNumber = 500;
self.timer = [NSTimer scheduledTimerWithTimeInterval:5.0f
target:self
selector:#selector(incrementScore:)
userInfo:nil
repeats:YES];
}
}
Then create a method that does the increment:
- (void)incrementScore:(NSTimer *)timer
{
self.bNumber++;
NSLog(#"Number = %d", self.bNumber);
}
Don't forget to invalidate the timer when you are done:
- (void)dealloc
{
// If not using arc don't forget super dealloc
[super dealloc];
[self.timer invalidate];
self.timer = nil;
}
That is one way. If you want to use the update method in cocos2d then you need to be keeping track of the accumulated delta. Once that value has reached or exceeded the total number of milliseconds in 5 seconds, then you add 1 to the number. Delta is the number of milliseconds that have passed since the last update. So for example the properties will be:
#property (nonatomic, assign) int bNumber;
#property (nonatomic, assign) CCTime totalDelta;
Then in update you would do the following (there are 1000 milliseconds in a second):
- (void)update:(CCTime)delta
{
const CCTime FiveSeconds = 5000.0f;
self.totalDelta += delta;
if (self.totalDelta >= FiveSeconds)
{
self.totalDelta = 0;
self.bNumber++;
NSLog(#"Number = %d", self.bNumber);
}
}
I hope this helped. What you are trying to do is pretty simply so I recommend brushing up on Obj-C programming before jumping into making games.
I think method [self schedule:#selector(methodName) interval:intervalValue]; is your choice.
Code sample:
```
static CGFloat playTime = 0.0;
#implementation GameScene {
}
-(void)onEnter
{
CCLOG(#"on enter");
[self schedule:#selector(runTimer) interval:5.0];
[super onEnter];
}
-(void) runTimer {
playTime += 1;
}
```
I am developing an iPAD application and I want to auto save the contents of the form into SQLITE in every 10 sec intervals. Right now if I press the save button then it saves to the database. Is there any way to auto save the whatever I am writing in the form in every 10-15 seconds. Help me out with this.
Use NSTimer and perform the save every x minutes. The code will look something like this. It is a modified version of the code here.
#interface MyController : UIViewController
{
#private
NSTimer * countdownTimer;
NSUInteger remainingTicks;
}
-(IBAction)doCountdown: (id)sender;
-(void)handleTimerTick;
-(void) saveData;
#end
#implementation MyController
// { your own lifecycle code here.... }
-(IBAction)doCountdown: (id)sender
{
if (countdownTimer)
return;
remainingTicks = 60;
[self saveData];
countdownTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: #selector(handleTimerTick) userInfo: nil repeats: YES];
}
-(void)handleTimerTick
{
remainingTicks--;
[self updateLabel];
if (remainingTicks <= 0) {
[countdownTimer invalidate];
countdownTimer = nil;
}
}
-(void) saveData
{
//Save your data here
}
#end
I'm trying to keep a timer running on another page when you switch to other pages and complete other tasks, in essence keeping a clock on how long it takes to do the tasks. Whenever I switch to another page, it resets the timer back to what it was started, and does the same with some switches on other pages that I'm trying to keep on. Any ideas?
Screenshot of storyboards:
Code so far:
//
// ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)start{
ticker = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self ``selector:#selector(showActivity) userInfo:nil repeats:YES];
}
- (IBAction)reset{
[ticker invalidate];
time.text = #" 0:00";
}
- (void)showActivity{
int currentTime = [time.text intValue];
int newTime = currentTime + 1;
time.text = [NSString stringWithFormat:#"%d", newTime];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
// ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
IBOutlet UILabel *time;
NSTimer *ticker;
}
- (IBAction)start;
- (IBAction)reset;
- (void)showActivity;
#end
Your NSTimer is a member variable of your view controller class. I'm assuming that when you switch between views, you're destroying this view controller and instantiating an instance of a new one. That means this view controller is gone, as well as the timer; it's not that the timer is being reset, it's that your old timer has been destroyed an a new one is being created.
What you need is to store your NSTimer and its functionality in a place where it will not be destroyed every time you change your view controller. One solution is to create a Singleton class which handles the timer. (A Singleton class is a class that can only be created one time; only one instance of it can exist. You can read more about them here.)
Here is an example of how you can create a Singleton class in Objective-C. The header:
//ApplicationManager.h
#interface ApplicationManager : NSObject
+(ApplicationManager*) instance;
#end
And the implementation:
//ApplicationManager.m
#import "ApplicationManager.h"
#implementation ApplicationManager
static ApplicationManager* appMgr = nil;
+(ApplicationManager*) instance
{
#synchronized([ApplicationManager class])
{
if(!appMgr)
{
appMgr = [[self alloc] init];
}
return appMgr;
}
return nil;
}
+(id) alloc
{
#synchronized([ApplicationManager class])
{
NSAssert((appMgr == nil), #"Only one instance of singleton class may be instantiated.");
appMgr = [super alloc];
return appMgr;
}
}
-(id) init
{
if(!(self = [super init]))
{
[self release];
return nil;
}
return self;
}
#end
The first time you call the instance method, the instance of the ApplicationManager will be created. Each time you want to access it, call the instance method again; the ApplicationManager will be returned. Now you simply add your NSTimer (and any other object you wish to persist throughout your application) as member variables of the ApplicationManager class.
You then must import the ApplicationManager class into your view controller class, and your view controller methods will change to:
-(IBAction) start
{
[[ApplicationManager instance] setTicker:[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(showActivity) userInfo:nil repeats:YES]];
}
-(IBAction) reset
{
[[[ApplicationManager instance] ticker] invalidate];
time.text = #" 0:00";
}
-(void) showActivity
{
int currentTime = [time.text intValue];
int newTime = currentTime + 1;
time.text = [NSString stringWithFormat:#"%d", newTime];
}
If you want to make things nice and neat, you can also add this line to the top of your ApplicationManager class:
#define APPMGR [ApplicationManager instance]
Now instead of having to type [ApplicationManager instance] everywhere, you can simply refer to it as APPMGR instead. [APPMGR ticker] is a lot cleaner than [[ApplicationManager instance] ticker] :)