I have a countdown timer where the user can input the time they want to start from using a countdown timer like in the clock app. The problem is, I can't figure out how to make the timer actually count down. I have already made the UI and have most of the code, but I don't know what would go in the updateTimer method I have. Here is my code:
- (void)updateTimer
{
//I don't know what goes here to make the timer decrease...
}
- (IBAction)btnStartPressed:(id)sender {
pkrTime.hidden = YES; //this is the timer picker
btnStart.hidden = YES;
btnStop.hidden = NO;
// Create the timer that fires every 60 sec
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
- (IBAction)btnStopPressed:(id)sender {
pkrTime.hidden = NO;
btnStart.hidden = NO;
btnStop.hidden = YES;
}
Please let me know what goes in the updateTimer method to let the timer decrease.
Thanks in advance.
You would track the overall time left with a variable. The updateTimer method will be called every second, and you would reduce the time left variable by 1 (one second) each time updateTimer method is called. I have given an example below, but I have renamed updateTimer to reduceTimeLeft.
SomeClass.h
#import <UIKit/UIKit.h>
#interface SomeClass : NSObject {
int timeLeft;
}
#property (nonatomic, strong) NSTimer *timer;
#end
SomeClass.m
#import "SomeClass.h"
#implementation SomeClass
- (IBAction)btnStartPressed:(id)sender {
//Start countdown with 2 minutes on the clock.
timeLeft = 120;
pkrTime.hidden = YES;
btnStart.hidden = YES;
btnStop.hidden = NO;
//Fire this timer every second.
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(reduceTimeLeft:)
userInfo:nil
repeats:YES];
}
- (void)reduceTimeLeft:(NSTimer *)timer {
//Countown timeleft by a second each time this function is called
timeLeft--;
//When timer ends stop timer, and hide stop buttons
if (timeLeft == 0) {
pkrTime.hidden = NO;
btnStart.hidden = NO;
btnStop.hidden = YES;
[self.timer invalidate];
}
NSLog(#"Time Left In Seconds: %i",timeLeft);
}
- (IBAction)btnStopPressed:(id)sender {
//Manually stop timer
pkrTime.hidden = NO;
btnStart.hidden = NO;
btnStop.hidden = YES;
[self.timer invalidate];
}
#end
Related
I make a Cocoa application.I create a NSTimer,it can work,but can't stop with invalidate.The following is my code.Anyone else can help me?
#implementation Document
{
NSTimeInterval elapsedTime;
NSTimer *timer;
}
- (IBAction)start:(id)sender
{
elapsedTime = 0.002f;
dispatch_async(dispatch_get_main_queue(), ^{
if(timer == nil)
{
NSLog(#"Starting");
captureFrame = [[CaptureFrame alloc] init];//captureFrame is another object
timer = [NSTimer scheduledTimerWithTimeInterval:elapsedTime target:captureFrame selector:#selector(sendSingleImageImage) userInfo:nil repeats:YES];
NSThread *thread = [NSThread currentThread];
NSLog(#"Current thread is%#",thread);// I want to see the thread.
}
else
{
NSLog(#"Exist!");
[timer invalidate];
timer = nil;
}
});
[_stopButton setEnabled:YES];
[_startButton setEnabled:NO];
}
- (IBAction)stop:(id)sender
{
dispatch_async(dispatch_get_main_queue(), ^{
[timer invalidate];
timer = nil;
NSThread *sthread = [NSThread currentThread];
NSLog(#"S thread is %#",sthread);// I want to see the thread
});
[_startcastButton setEnabled:YES];
[_stopButton setEnabled:NO];
NSLog(#"Stopping");
}
I tried use the following method,but didn't work.
dispatch_async(dispatch_get_main_queue(), ^(void)block)
I also tried to find whether two actions in the same thread,and the result is yes.
So please tell me how to stop the NSTimer.
Any help is appreciated,thanks a lot in advance.
In the following code, the NSTimer interval is set at 1 second between each picture. My goal is to change the interval after the first two pictures, hello.png and bye.png, to 4 seconds.
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *imageNames = #[#"hello.png",#"bye.png",#"helloagain.png",#"bye again"];
self.images = [[NSMutableArray alloc] init];
for (int i = 0; i < imageNames.count; i++) {
[self.images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
self.animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 95, 86, 90)];
self.animationImageView.image = self.images[0];
[self.view addSubview:self.animationImageView];
self.animationImageView.userInteractionEnabled = TRUE;
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(changeImage:) userInfo:nil repeats:YES];
}
-(void)changeImage:(NSTimer *) timer {
if (counter == self.images.count - 1 ) {
counter = 0;
}else {
counter ++;
}
self.animationImageView.image = self.images[counter];
}
my goal is to change the interval
You cannot change a timer in any way. To change the firing interval, invalidate and destroy the timer and make a new timer with the new interval.
To do that, you will need to have kept a reference to the timer, as a property of your view controller (something that you have fatally failed to do in your implementation - you would have had no way to stop the timer, and you would have crashed if your view controller ever went out of existence, or else your view controller would have leaked because the timer retains it).
Make the timer object a member variable. Initially set animation time as 1 second. In the callback invalidate the timer and create a new one with 1 or 4 seconds depending on the counter.
#interface ViewController ()
#property (strong,nonatomic) NSMutableArray *images;
#property (strong,nonatomic) UIImageView *animationImageView;
{
NSTimer *_timer;
}
#end
#implementation ViewController {
NSInteger counter;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *imageNames = #[#"hello.png",#"bye.png",#"helloagain.png",#"bye again"];
self.images = [[NSMutableArray alloc] init];
for (int i = 0; i < imageNames.count; i++) {
[self.images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
self.animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 95, 86, 90)];
self.animationImageView.image = self.images[0];
[self.view addSubview:self.animationImageView];
self.animationImageView.userInteractionEnabled = TRUE;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(bannerTapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self.animationImageView addGestureRecognizer:singleTap];
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(changeImage:) userInfo:nil repeats:YES];
}
-(void)changeImage:(NSTimer *) timer {
[_timer invalidate];
if (counter == self.images.count - 1 ) {
counter = 0;
}else {
counter ++;
}
if(counter == 0 || counter == 1)
{
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(changeImage:) userInfo:nil repeats:YES];
}
else if(counter == 2 || counter == 3)
{
_timer = [NSTimer scheduledTimerWithTimeInterval:4 target:self selector:#selector(changeImage:) userInfo:nil repeats:YES];
}
self.animationImageView.image = self.images[counter];
}
The best/simpliest way :
-(void)Timer{
// Do what you want here
// modify the frequency value if you want.
[NSTimer scheduledTimerWithTimeInterval:frequence target:self selector:#selector(Timer) userInfo:nil repeats:NO];
}
Frequence is a variable that contains the interval.
You just need to call Timer method by yourself the first time ;)
For you, it would give
-(void)changeImage{
if (counter == self.images.count - 1 )
counter = 0;
else
counter ++;
frequence = (counter < 2)?1:4;
self.animationImageView.image = self.images[counter];
[NSTimer scheduledTimerWithTimeInterval:frequence target:self selector:#selector(changeImage) userInfo:nil repeats:NO];
}
I have a NSTimer that works fine and counts down in 1 second intervals. But I want the timer to trigger immediately without that 1 second delay.
I thought calling [timer fire] should work for this (described here) but it doesn't make a difference. I want the timer to be triggered as fast as if I scheduled the interval to be 0.
- (void)onStartButtonPressed:(UIButton*)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:tv];
NSIndexPath* indexPath = [tv indexPathForRowAtPoint:buttonPosition];
NSInteger index = indexPath.row;
// starts timer for cell at the index path
if (indexPath != nil)
{
NSTimer* timer = [timerArray objectAtIndex: index];
if ([timer isEqual:[NSNull null]]) {
NSLog(#"It's empty");
// start timer
NSTimer timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(onTick:)
userInfo:indexPath
repeats:YES];
// [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
NSLog(#"before timer fire");
[timer fire];
// update data array of timer objects and countdowns
NSInteger selectedTimeIdx = [[selectedTimeIdxArray objectAtIndex: index] integerValue];
NSInteger selectedTime = [pickerTimeArray[selectedTimeIdx] integerValue];
[timerArray replaceObjectAtIndex:index withObject:timer];
[countdownArray replaceObjectAtIndex:index withObject:[NSNumber numberWithInteger:selectedTime*60]];
} else {
NSLog(#"It's not empty");
}
}
- (void)onTick:(NSTimer *)timer
{
NSLog(#"on tick method starts");
// get the timer's owner's index path and update label
NSIndexPath* indexPath = [timer userInfo];
NSInteger index = indexPath.row;
// update countdown
NSInteger countdown = [[countdownArray objectAtIndex: index] integerValue];
[countdownArray replaceObjectAtIndex:index withObject:[NSNumber numberWithInteger:--countdown]];
// NSLog(#"countdown: %ld", (long)countdown);
[tv reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
// NSLog(#"Tic indexPath: %#", indexPath);
if (countdown == 0)
{
[timer invalidate];
timer = nil;
}
}
The timer works but I don't want there to be a 1 second delay for it to be initially triggered. I want the timer to start immediately.
Edit: I added logs that rmaddy suggested. Here are my results (I changed the interval time to 3):
2015-05-19 14:41:02.827 restaurant[4206:77915] before timer fire
2015-05-19 14:41:02.827 restaurant[4206:77915] on tick method starts
2015-05-19 14:41:05.827 restaurant[4206:77915] on tick method starts
2015-05-19 14:41:08.828 restaurant[4206:77915] on tick method starts
Why not just call your method before the timer
[self onTick:nil];
//usual timer code here
Edit: as stated by rmaddy
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(onTick:)
userInfo:indexPath repeats:YES];
[self onTick:timer];
Some of the comments were really helpful, especially adding the logs as suggested by rmaddy. It turns out [timer fire] was working fine.
I had confused myself because my timerLabel which displayed the countdown was being updated in my cell with a 1 second delay, and I thought that meant the timer instantiation was delayed.
Once I saw what the problem really was, all I had to do was update the cell in the same block that I instantiated the timer (instead of just onTick).
I just added this to my onStartButtonPressed and everything works as I want it to.
// update countdown
NSInteger countdown = [[countdownArray objectAtIndex: index] integerValue];
[countdownArray replaceObjectAtIndex:index withObject:[NSNumber numberWithInteger:--countdown]];
// cell label gets updated right at start button press
[tv reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
Leaving here in case it helps anyone.
I have NSTimer to display an image after 2 seconds when i click on button. It is working fine when I Click on a Button, But the problem is When I double Click the Button the NSTimer is Not Stopping. Continuously it is displaying images(calling NSTimer method) without Clicking on the button for Next Time. How to stop the NSTimer when i double Click / more Clicks on that button at a time.
*This is my Code*
-(void)buttonClicked
{
timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(timerClicked) userInfo:nil repeats:YES];
imgviewtransparent.image = [UIImage imageNamed:[imagesArray objectAtIndex:i]];
}
-(void)timerClicked
{
[timer invalidate];
timer = nil;
}
With double Click it will schedule timer twice.
You can create a BOOL property to avoid this if you want.
-(void)viewDidLoad{
[super viewDidLoad];
//Define all your implementation
//Set BOOL property to no at start
self.isButtonClicked = NO; // You need to define this BOOL property as well.
}
-(void)buttonClicked {
// Return if one click is already in process
if (self.isButtonClicked) {
return;
}
self.isButtonClicked = YES:
timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self
selector:#selector(timerClicked) userInfo:nil repeats:YES];
imgviewtransparent.image = [UIImage imageNamed:[imagesArray objectAtIndex:i]];
}
-(void)timerClicked {
[timer invalidate];
timer = nil;
self.isButtonClicked = NO;
}
Here is my code. I expected the timer to stop in 5 second after it starts but it doesn't. What is wrong here ?
-(void)loadView
{
NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.0
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:YES];
if([NSDate timeIntervalSinceReferenceDate] - startTime >= 5) {
[timer invalidate];
}
}
-(void)targetMethod:(NSTimer *)timer {
NSLog(#"bla");
}
NSDate's timeIntervalSinceReferenceDate is, by default, returning January 1st, 2001. Subtracting the same values will always be 0.
Apple's documentation:
https://developer.apple.com/library/mac/ipad/#documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/Reference/Reference.html
Here's an idea:
In your .h
#interface MyClass : NSObject
#property (nonatomic, retain) NSTimer *timer;
- (void)targetMethod:(NSTimer *)timer;
- (void)cancelTimer;
#end
In your .m
#implementation MyClass
#synthesize timer;
-(void)loadView
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.0
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:YES];
[self performSelector:#selector(cancelTimer) withObject:nil afterDelay:5.0];
}
-(void)cancelTimer {
[self.timer invalidate];
}
-(void)targetMethod:(NSTimer *)timer {
NSLog(#"bla");
}
This is short and simple:
NSDate *endtime = [NSDate dateWithTimeIntervalSinceNow:5];
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(timerTick:)
userInfo:endtime
repeats:YES];
-(void)timerTick:(NSTimer*)timer
{
NSLog(#"timer tick");
if ( [timer.userInfo timeIntervalSinceNow] < 0 )
{
[timer invalidate];
NSLog(#"invalidating timer");
}
}
Your time difference is always 0 so you never invalidate it!
Try setting startTime before you set the timer.
You get the 'startTime' value and the value you compare it to are identical. Your calculation will always give 0. You must store the 'startTime' in your loadView method and then use it in the calculation.