This question already has an answer here:
Adding pause functionality for NSTimer
(1 answer)
Closed 9 years ago.
In the project I am working on I need to have a stopwatch that will pause and continue. So far All of the basic functions work, but I have not been able to find a way to pause the timer and re-start it. FYI, I have already checked the other postings and they didn't work. Code:
.h:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface Timer : UIViewController <AVAudioRecorderDelegate, AVAudioPlayerDelegate>
{
AVAudioRecorder *recorder;
AVAudioPlayer *player;
}
#property (weak, nonatomic) IBOutlet UIButton *recordPauseButton;
#property (weak, nonatomic) IBOutlet UIButton *stopButton;
#property (weak, nonatomic) IBOutlet UILabel *stopwatchLabel;
-(IBAction)recordPauseTapped:(id)sender;
-(IBAction)stopTapped:(id)sender;
#end
.m:
#import "Timer.h"
#interface SongIdeasRecording ()
#property (strong, nonatomic) NSTimer *stopWatchTimer; // Store the timer that fires after a certain time
#property (strong, nonatomic) NSDate *startDate; // Stores the date of the click on the start button
#end
#implementation Timer
#synthesize stopButton, playButton, recordPauseButton, stopwatchLabel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)updateTimer
{
// Timer is 1/10 of a second so thats what we add to stopwatch
NSTimeInterval timeInterval = 0.1;
// Create a date formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.SSS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
// Take the time currently displayed on the stopwatch and add the time interval to it
NSDate *oldDate = [dateFormatter dateFromString:self.stopwatchLabel.text];
NSDate *newDate = [oldDate dateByAddingTimeInterval:timeInterval];
//Get a string representation of the new date
NSString *timeString = [dateFormatter stringFromDate:newDate];
self.stopwatchLabel.text = timeString;
}
- (IBAction)recordPauseTapped:(id)sender {
self.startDate = [NSDate date];
// Create the stop watch timer that fires every 100 ms
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
// Stop the audio player before recording
if (player.playing) {
[player stop];
}
if (!recorder.recording) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording
[recorder record];
[recordPauseButton setTitle:#"Pause" forState:UIControlStateNormal];
} else {
// Pause recording
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
[recorder pause];
[recordPauseButton setTitle:#"Record" forState:UIControlStateNormal];
}
[stopButton setEnabled:YES];
[playButton setEnabled:NO];
}
- (IBAction)stopTapped:(id)sender {
[recorder stop];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setActive:NO error:nil];
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
}
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully: (BOOL)flag{
[recordPauseButton setTitle:#"Record" forState:UIControlStateNormal];
[stopButton setEnabled:NO];
[playButton setEnabled:YES];
}
- (IBAction)playTapped:(id)sender {
if (!recorder.recording){
player = [[AVAudioPlayer alloc] initWithContentsOfURL:recorder.url error:nil];
[player setDelegate:self];
[player play];
self.startDate = [NSDate date];
stopwatchLabel.text = #"00:00:00.000";
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
}
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
}
#end
In your case, you are calculating the value of the stopwatch label with the NSDate that the record button was originally pressed. There is no way to pause the timer in this way, as for every time you recalculate the value of the stopwatch label, it will reflect the original date of which the record button was pressed. I would recommend changing this method to something like this:
- (void)updateTimer
{
// Timer is 1/10 of a second so thats what we add to stopwatch
NSTimeInterval timeInterval = 0.1;
// Create a date formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.SSS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
// Take the time currently displayed on the stopwatch and add the time interval to it
NSDate *oldDate = [dateFormatter dateFromString:self.stopwatchLabel.text];
NSDate *newDate = [oldDate dateByAddingTimeInterval:timeInterval];
//Get a string representation of the new date and BOOM POW.
NSString *timeString = [dateFormatter stringFromDate:newDate];
self.stopwatchLabel.text = timeString;
}
Have not tested this but I hope it works. I wouldn't be surprised if there were some syntax issues too. Also, make sure the string that is in self.stopwatchLabel.text follows the format to start (Ex. 00:00:00.000).
Try commenting your code inside - (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player.
My guess is that in - (IBAction)recordPauseTapped:(id)sender you're calling [player stop], which triggers - (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag which invalidates your new timer.
- (IBAction)recordPauseTapped:(id)sender {
if ([stopwatchLabel.text isEqual: #"00:00:00.000"]) {
self.startDate = [NSDate date];
// Create the stop watch timer that fires every 100 ms
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
// Stop the audio player before recording
if (player.playing) {
[player stop];
}
if (!recorder.recording) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording
[recorder record];
[recordPauseButton setTitle:#"Pause" forState:UIControlStateNormal];
}
}else {
// Pause recording
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
[recorder pause];
[recordPauseButton setTitle:#"Record" forState:UIControlStateNormal];
}
[stopButton setEnabled:YES];
[playButton setEnabled:NO];
}
Related
---Hello I'm new to objective C. I am not able to record audio while pressing buttonTapped method below listed- buttontapped is disabled while application is launched , thus i cannot record and hence I cannot play the recorded sound. kindly guide with this.
--Here is my files.
-- I have imported two files of CircularProgressBarTimer from github and imported those file in the .h file.
----This is my .h file
#interface ViewController : UIViewController<AVAudioRecorderDelegate,AVAudioPlayerDelegate,UIGestureRecognizerDelegate>{
NSTimer *timer;
NSInteger globalTimer;
NSInteger counter;
NSInteger minutesLeft;
NSInteger secondsLeft;
UIRefreshControl *refreshControl;
CircularProgressTimer *progressTimerView;
}
#property (weak, nonatomic) IBOutlet UIButton *buttonRecord;
#property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *longpressGesture;
#property (weak, nonatomic) IBOutlet CircularProgressTimer *circularProgressView;
- (IBAction)buttonTapped:(id)sender;
-(void)longPressed:(UIGestureRecognizer *)longPress;
And this is my .m file
#interface ViewController (){
AVAudioRecorder *recorder;
AVAudioPlayer *player;
}
#end
--In my viewDidLoad method i have initialise audio player, longesturerecognizer
#implementation ViewController
#synthesize buttonRecord;
#synthesize longpressGesture;
#synthesize circularProgressView;
- (void)viewDidLoad {
[super viewDidLoad];
UILongPressGestureRecognizer *gesture1 = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPressed:)];
gesture1.delegate =self;
[gesture1 setMinimumPressDuration:(NSTimeInterval)10];
[self.buttonRecord addGestureRecognizer:gesture1];
//Set the audio file
NSArray *pathComponents =[NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject],#"MyAudioDemo.m4a", nil];
NSURL *outputFileURL =[NSURL fileURLWithPathComponents:pathComponents];
//Set the audio Session
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
//Define the recorder Setting
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc]init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
[recordSetting setValue :[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
[recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey];
//Initiate and prepare the recorder
recorder=[[AVAudioRecorder alloc]initWithURL:outputFileURL settings:recordSetting error:nil];
recorder.delegate=self;
recorder.meteringEnabled =YES;
[recorder prepareToRecord];
//Disable play button when application launches
[buttonRecord setEnabled:NO];
}
---And i have aldo define the circularprogress bar method about the size allocation.
- (void)drawCircularProgressBarWithMinutesLeft:(NSInteger)minutes secondsLeft:(NSInteger)seconds
{
// Removing unused view to prevent them from stacking up
for (id subView in [self.view subviews]) {
if ([subView isKindOfClass:[CircularProgressTimer class]]) {
[subView removeFromSuperview];
}
}
// Init our view and set current circular progress bar value
CGRect progressBarFrame = CGRectMake(0, 0, 180, 180);
progressTimerView = [[CircularProgressTimer alloc] initWithFrame:progressBarFrame];
[progressTimerView setCenter:CGPointMake(160, 210)];
[progressTimerView setPercent:seconds];
if (/*minutes == 0 &&*/ seconds == 0) {
[progressTimerView setInstanceColor:[UIColor redColor]];
}
// Here, setting the minutes left before adding it to the parent view
//[progressTimerView setMinutesLeft:minutesLeft];
[progressTimerView setSecondsLeft:secondsLeft];
[self.view addSubview:progressTimerView];
progressTimerView = nil;
}
- (void)startTimer
{
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateCircularProgressBar)
userInfo:nil
repeats:YES];
}
- (void)updateCircularProgressBar
{
// Values to be passed on to Circular Progress Bar
if (globalTimer > 0 && globalTimer <= 1200) {
globalTimer--;
// minutesLeft = globalTimer / 60;
secondsLeft = globalTimer % 43;
[self drawCircularProgressBarWithMinutesLeft:minutesLeft secondsLeft:secondsLeft];
NSLog(#"Time left:%02ld", (long)secondsLeft);
} else {
[self drawCircularProgressBarWithMinutesLeft:0 secondsLeft:0];
[timer invalidate];
}
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event{
switch (event.subtype) {
case UIEventSubtypeRemoteControlPause:
[self.circularProgressView pause];
break;
case UIEventSubtypeRemoteControlPlay:
[self.circularProgressView play];
default:
break;
}
}
- (BOOL)shouldAutorotate{
return YES;
}
----I know I have done wrong code in buttonRecord event. but when I debug the application the button is disabled and cannot be pressed. Please guide me with this, that would be appreciated.
- (IBAction)buttonTapped:(id)sender {
if (player.playing) {
[player stop];
}
if (!recorder.recording) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording
[recorder record];
[buttonRecord setTitle:#"Pause" forState:UIControlStateNormal];
} else {
// Pause recording
[recorder pause];
[buttonRecord setTitle:#"Record" forState:UIControlStateNormal];
}
//[stopButton setEnabled:YES];
[_playButton setEnabled:NO];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateCircularProgressBar)
userInfo:nil
repeats:YES];
NSLog(#"playTapped");
if (!recorder.recording) {
player =[[AVAudioPlayer alloc]initWithContentsOfURL:recorder.url error:nil];
//[player setDelegate:self];
[buttonRecord setEnabled:YES];
[recorder recordForDuration:(NSTimeInterval)10];
[recorder record];
}
}
- (IBAction)playbuttontTapped:(id)sender {
if (!recorder.recording){
player = [[AVAudioPlayer alloc] initWithContentsOfURL:recorder.url error:nil];
[player setDelegate:self];
[player play];
}}
The last line in your viewDidLoad is causing your record button to be dissabled, why have yo added this [buttonRecord setEnabled:NO];
You may try changing it to [buttonRecord setEnabled:YES]; or removing that line completely
I am using the AVAudioPlayer class to play audio. I have implemented a timer slider that progresses as the music is playing.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
AudioBool = YES;
}
- (IBAction)play:(id)sender
{
// Code to read the file from resource folder and sets it in the AVAudioPlayer
// Sets the audio timer in 1 sec intervals
sliderTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime) userInfo:nil repeats:YES];
// Sets the slider maximum value
slider.maximumValue = player.duration;
// Sets the valueChanged target
[slider addTarget:self action:#selector(sliderChanged : ) forControlEvents : UIControlEventValueChanged];
// Play the audio
// [player prepareToPlay];
[player play];
if(AudioBool == YES)
{
[player play];
AudioBool = NO;
}
else
{
[player pause];
AudioBool = YES;
}
}
- (void)updateTime
{
// Updates the slider about the music time
slider.value = player.currentTime;
NSString *time = [self timeFormatted:slider.value];
self.timerLabe.text = time;
}
- (NSString *)timeFormatted:(int)totalSeconds
{
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
//int hours = totalSeconds / 3600;
//return [NSString stringWithFormat:#"%02d:%02d:%02d",hours, minutes, seconds];
return [NSString stringWithFormat:#"%02d:%02d", minutes, seconds];
}
- (IBAction)sliderChanged : (UISlider *)sender
{
// skips music with slider changged
[player pause];
[player setCurrentTime:slider.value];
// [player prepareToPlay];
[player play];
}
// Stops the timer when audio finishes
- (void)audioPlayerDidFinishPlaying : (AVAudioPlayer *)player successfully :
(BOOL)flag
{
// Music completed
if (flag)
{
[sliderTimer invalidate];
}
}
2 issues I have:
I can't seem to pause the audio. When I re-tap the play button, it re-starts the audio at the beginning instead of pausing it.
The slider also re-starts at the beginning instead of pausing.
How do I fix these issues?
Thanks
try this solution, you need to make changes in play method basically..shift the slider initialisation in viewDidLoad also play/pause based on isPlaying property (AudioBool property in your code)
#import <AVFoundation/AVFoundation.h>
#interface ViewController ()
#property (nonatomic, strong) AVAudioPlayer *audioPlayer;
#property (nonatomic) BOOL isPlaying;
#property (nonatomic, strong) NSTimer *sliderTimer;
#property (weak, nonatomic) IBOutlet UISlider *slider;
#property (weak, nonatomic) IBOutlet UILabel *timerLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSBundle *mainBundle = [NSBundle mainBundle];
NSString *filePath = [mainBundle pathForResource:#"10101" ofType:#"mp3"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
self.isPlaying = NO;
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
[self.audioPlayer prepareToPlay];
[self.slider addTarget:self action:#selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
self.slider.minimumValue = 0;
self.slider.maximumValue = self.audioPlayer.duration;
}
- (IBAction)play:(id)sender {
if (self.isPlaying)
{
// Music is currently playing
[self.audioPlayer pause];
self.isPlaying = !self.isPlaying;
}
else
{
// Music is currenty paused/stopped
[self.audioPlayer play];
self.isPlaying = !self.isPlaying;
self.sliderTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime) userInfo:nil repeats:YES];
}
}
- (void)sliderChanged:(UISlider *)sender
{
// skips music with slider changged
[self.audioPlayer pause];
[self.audioPlayer setCurrentTime:self.slider.value];
[self.audioPlayer play];
}
- (void)updateTime
{
// Updates the slider about the music time
self.slider.value = self.audioPlayer.currentTime;
NSString *time = [self timeFormatted:self.slider.value];
self.timerLabel.text = time;
}
- (NSString *)timeFormatted:(int)totalSeconds
{
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
return [NSString stringWithFormat:#"%02d:%02d", minutes, seconds];
}
// Stops the timer when audio finishes
- (void)audioPlayerDidFinishPlaying : (AVAudioPlayer *)player successfully:(BOOL)flag
{
// Music completed
if (flag)
{
[self.sliderTimer invalidate];
}
}
I have two view controllers, both connected via Segue and using Storyboard.
In view controller 1 I have an NSTimer counting up and updating a UILabel.
When I switch to view controller 2 and back to 1 the uilabel is no longer updated.
Here is some code:
headerfile
NSString *timerTicksForCounter;
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateTimerLabel];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateTimerLabel];
}
- (void) startLastConUpdater
{
lastCTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
-(void) updateTimerLabel
{
NSLog(#"timer: %#", timerTicksForCounter);
if (timerTicksForCounter) {
NSLog(#"timer not null");
mainTimerLabel.text = timerTicksForCounter;
}
}
- (void)updateTimer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:stopDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
timerTicksForCounter = [dateFormatter stringFromDate:timerDate];
[self updateTimerLabel];
}
What do you mean it's no longer updated ? Does this mean you lose what was displayed before switching or it doesn't update anymore. If it's not updating anymore it's because you don't start the timer in the appropriate method. You could do something like :
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self startLastConUpdater];
}
This should solve both issues I mentioned above.
I have a uitextlabel that is updated using an nstimer.
When I switch to another view controller (storyboard segue) and back again the text label is no longer updated (returns to default text), even though the timer continues to run.
The timer is writing a value to the uitextlabel which stops working after switching.
NOTE: the updateTimerLabel method continues to output the correct info but the label is not updated.
headerfile
NSString *timerTicksForCounter;
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateTimerLabel];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateTimerLabel];
}
- (void) startLastConUpdater
{
lastCTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
-(void) updateTimerLabel
{
NSLog(#"timer: %#", timerTicksForCounter);
if (timerTicksForCounter) {
NSLog(#"timer not null");
mainTimerLabel.text = timerTicksForCounter;
}
}
- (void)updateTimer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:stopDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
timerTicksForCounter = [dateFormatter stringFromDate:timerDate];
[self updateTimerLabel];
}
update your textField text in
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateLabel];
}
First Declare
NSTimer * countdownTimer;
NSUInteger remainingTicks;
- (void)viewDidLoad
{
[super viewDidLoad];
remainingTicks = 60;
[self updateLabel];
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)updateLabel
{
timeLabel.text = [[NSNumber numberWithUnsignedInt: remainingTicks] stringValue];
}
//sent notification
[[NSNotificationCenter defaultCenter] removeObserver:self];
**//Get (Retrive) Notification**
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
//Get notification method.
- (void) receiveTestNotification:(NSNotification *) notification
{
// [notification name] should always be #"TestNotification"
// unless you use this method for observation of other notifications
// as well.
if ([[notification name] isEqualToString:#"TestNotification"])
NSLog (#"Successfully received the test notification!");
**//Here write your code for update textfield**
}
I have a simple app that has an NSTimer object in the appDelegate to be accessed by all views. The structure of the app is with a UINavigationController. When I fire the NSTimer object, my UILabel is being updated with the correct countdown function, but when I go back to the rootViewController and back to the countdown timer view, my UILabel is being updated with the current countdown time, but no subsequent updates to the UILabel happen. What am I missing? I have done research on making sure the UILabel object is not nil, that I call the function on the viewDidAppear method, and nothing seems to work! Here is the code:
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
}
#property (nonatomic, retain) NSTimer *countdownTimer;
AppDelegate.m
#implementation AppDelegate
#synthesize countdownTimer;
CountdownTimerViewController.h
#import "AppDelegate.h"
enter code here
#interface CountdownTimerViewController : UIViewController {
enter code here
AppDelegate *appdelegate;
}
#property (strong, nonatomic) IBOutlet UILabel *labelCountdownTimer;
#property (strong, nonatomic) IBOutlet UIButton *buttonStartTimer;
#property (strong, nonatomic) IBOutlet UIButton *buttonStopTimer;
- (IBAction)startTimer:(id)sender;
- (IBAction)stopTimer:(id)sender;
CountdownTimerViewController.m
#implementation CountdownTimerViewController
#synthesize labelCountdownTimer;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//Instatiating Appdelegate
if(!appdelegate)
appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void) viewDidAppear:(BOOL)animated {
if ([appdelegate.countdownTimer isValid]) {
[self countDown];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Button Action Methods
- (IBAction)startTimer:(id)sender {
[self updateCounter];
}
- (IBAction)stopTimer:(id)sender {
[appdelegate.countdownTimer invalidate];
labelCountdownTimer.text = #"00:00:00";
}
int countLimit=30; //seconds
NSDate *startDate;
- (void)countDown {
if([[NSDate date] timeIntervalSinceDate:startDate] >= countLimit) {
[appdelegate.countdownTimer invalidate];
return;
}
else {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = -([currentDate timeIntervalSinceDate:startDate]);
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString = [dateFormatter stringFromDate:timerDate];
NSLog(#"timeString: %#",timeString);
NSLog(#"labelCountdownTimer: %#",labelCountdownTimer);
labelCountdownTimer.text = timeString;
}
}
- (void)updateCounter {
labelCountdownTimer.text = #"00:00:00";
startDate = [NSDate date];
appdelegate.countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(countDown)
userInfo:nil
repeats:YES];
}
Thanks to everyone for your comments. I actually solved it by performing a method that will go and retrieve the value that the NSTimer is updating in my AppDelegate, since the method firing the NSTimer is no longer in the main thread when I leave the view and come back to it. This method will loop as long as my NSTimer is valid. I also placed a delay, allowing for the UI to update the value, and then perform the method again. Here is the code in case it helps someone running into a similar issue. I got this idea from the suggestion provided by chandan, thanks!!
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
}
#property (nonatomic, retain) NSTimer *countdownTimer;
#property (nonatomic, retain) NSString *timeString;
AppDelegate.m
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
}
#property (nonatomic, retain) NSTimer *countdownTimer;
#property (nonatomic, retain) NSString *timeString;
CountdownTimerViewController.h
#interface CountdownTimerViewController : UIViewController {
AppDelegate *appdelegate;
}
#property (strong, nonatomic) IBOutlet UILabel *labelCountdownTimer;
#property (strong, nonatomic) IBOutlet UIButton *buttonStartTimer;
#property (strong, nonatomic) IBOutlet UIButton *buttonStopTimer;
- (IBAction)startTimer:(id)sender;
- (IBAction)stopTimer:(id)sender;
CountdownTimerViewController.m
#implementation CountdownTimerViewController
#synthesize labelCountdownTimer;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//Instatiating Appdelegate
if(!appdelegate)
appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void) viewDidAppear:(BOOL)animated {
if ([appdelegate.countdownTimer isValid]) {
[self updateLabel];
} else {
labelCountdownTimer.text = #"00:00:00";
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Button Action Methods
- (IBAction)startTimer:(id)sender {
[self updateCounter];
}
- (IBAction)stopTimer:(id)sender {
[appdelegate.countdownTimer invalidate];
labelCountdownTimer.text = #"00:00:00";
}
int countLimit=30; //seconds
NSDate *startDate;
- (void)updateCounter {
labelCountdownTimer.text = #"00:00:00";
startDate = [NSDate date];
appdelegate.countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(countDown)
userInfo:nil
repeats:YES];
}
- (void)countDown {
if([[NSDate date] timeIntervalSinceDate:startDate] >= countLimit) {
[appdelegate.countdownTimer invalidate];
return;
}
else {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = -([currentDate timeIntervalSinceDate:startDate]);
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
appdelegate.timeString = [dateFormatter stringFromDate:timerDate];
labelCountdownTimer.text = appdelegate.timeString;
}
}
- (void) updateLabel {
if ([appdelegate.countdownTimer isValid]) {
labelCountdownTimer.text = appdelegate.timeString;
[self performSelector:#selector(updateLabel) withObject:nil afterDelay:0.05];
}
}
Type casting like this:
appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
won't actually make the UIApplicationDelegate into your AppDelegate class and add your extra parameters. Hence there will be no pointer to the timer in this variable.
You need a different approach for storing the timer pointer.
Try to update text on UILabel on main thread. Sometimes updation in UILabel not working on backgound thread.
- (void) viewDidAppear:(BOOL)animated
{
if ([appdelegate.countdownTimer isValid])
{
[self performSelectorOnMainThread:#selector(countDown) withObject:nil waitUntilDone:NO];
}
}
If your appdelegate object is working fine and UILabel is being updated with the current countdown time, but no subsequent updates to the UILabel happen then apply UI changes on main thread like it
- (void)countDown {
[self performSelectorOnMainThread:#selector(changeCountDownValue) withObject:nil waitUntilDone:NO];
}
- (void)changeCountDownValue
{
if([[NSDate date] timeIntervalSinceDate:startDate] >= countLimit) {
[appdelegate.countdownTimer invalidate];
return;
}
else {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = -([currentDate timeIntervalSinceDate:startDate]);
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString = [dateFormatter stringFromDate:timerDate];
NSLog(#"timeString: %#",timeString);
NSLog(#"labelCountdownTimer: %#",labelCountdownTimer);
labelCountdownTimer.text = timeString;
}
}
please double check with NSTimer object. It should be working fine for UILabel updation. Please let me know if any problem still occurring.