Resuming a countdown when poping to root view - ios

I am new to iPhone Development, and am making a countdown project using navigation controller and storyboard. My application has two views. The first view only has one button. When this button is clicked, it goes to the second view. The second view has the countdown objects. My problem is that when the countdown is running in second view, if I go back to first view and then hit the button to go to the second view, the countdown is no longer running.
Here is the code:
view1.h
#import <UIKit/UIKit.h>
#import "view2.h"
#interface view1: UIViewController
-(IBAction)nextpage:(id)sender;
#end
view1.m
#implementation view1
-(IBAction)nextpage:(id)sender
{
view2 *next=[self.storyboard instantiateViewControllerWithIdentifier:#"secondview"];
[self.navigationController pushViewController:next animated:YES];
}
#end
view2.h
#interface view2 : UIViewController
{
IBOutlet UILabel *lbl;
IBOutlet UITextField *field;
NSTimer *theTimer;
NSDate *targetDate;
NSCalendar *cal;
NSDateComponents *components;
}
#property (nonatomic,retain) IBOutlet UILabel *lbl;
#property (nonatomic,retain) IBOutlet UITextField *field;
-(IBAction)back_first_view;
#end
view2.m
#implementation view2
#synthesize lbl,field;
- (void)viewDidLoad
{
cal = [[NSCalendar currentCalendar] retain];
components = [[NSDateComponents alloc] init];
}
- (IBAction)buttonPressed:(id)sender {
if (theTimer != nil) {
return;
}
NSString *input = field.text;
NSArray *timeSplit = [input componentsSeparatedByString:#":"];
NSUInteger hours = [[timeSplit objectAtIndex:0] intValue];
NSUInteger minutes = [[timeSplit objectAtIndex:1] intValue];
NSDate *now = [NSDate date];
NSDateComponents *dateComponents = [cal components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:now];
[dateComponents setHour:hours];
[dateComponents setMinute:minutes];
if (!targetDate) {
targetDate = [[cal dateFromComponents:dateComponents] retain];
}
else {
targetDate = nil;
targetDate = [[cal dateFromComponents:dateComponents] retain];
}
if ([targetDate timeIntervalSinceNow] > 0) {
theTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(tick) userInfo:nil repeats:YES];
[self hideKeyboard];
}
else {
targetDate = nil;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Cannot countdown because time is before now" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
}
- (void)tick {
if ([targetDate timeIntervalSinceNow] <= 0) {
//Checks if the countdown completed
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Countdown Completed" message:#"YAY! The countdown has complete" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
components = [cal components:(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:[NSDate date] toDate:targetDate options:0];
NSInteger hours = [components hour];
NSInteger minutes = [components minute];
NSInteger seconds = [components second];
NSString *output = [NSString stringWithFormat:#"%i Hours\n%i Minutes\n%i Seconds\n", hours, minutes, seconds];
lbl.text = output;
}
- (void)hideKeyboard {
if ([field isFirstResponder]) [field resignFirstResponder];
}
- (IBAction)back_first_view
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
#end

Change this method to,
- (IBAction)nextpage:(id)sender
{
[self.navigationController pushViewController:self.next animated:YES];
}
And add the first line to your viewDidLoad or any such methods,
- (void)viewDidLoad {
[super viewDidLoad]; //Always keep super call as first call in a method
self.next = [self.storyboard instantiateViewControllerWithIdentifier:#"secondview"];
// Do any additional setup after loading the view.
}
And declare next in .h file #interface as,
#interface view1 : UIViewController
{
}
#property (nonatomic, strong) view2 *next;
Each and and every time when you are going to next page, you were creating a new view2, which you need to change.

Because you're using a UINavigationController, when you navigate back from View2 to View1, the View2 instance (and the timer running on it) is destroyed. When you navigate to View2 a second time, it's a brand-new instance.
For this to behave as you expect, you have to keep the timer alive somehow. You could modify your app delegate to manage the timer, and have View2 get its timer info from the delegate (for example).

Related

Elapsed time and setting a date?

Here is what I'm trying to accomplish: User touches a button, and and can share something to facebook. The date that the user touched the button is set, and for 24 seconds the user cannot share anything to facebook again. After 24 seconds, the user then is allowed to share another post to facebook.
The error is not facebook related, but it has something to do with the elapsedTime. After the elapsedTime is reset, it doesn't count back up to 24. It just stays at a very small number like "0.0043"
Here's my code:
-(void)postToFacebook
{
if (elapsedTime >= 24) {
[[CCDirector sharedDirector] pause];
[[CCDirector sharedDirector] stopAnimation];
CCAppDelegate *app = (CCAppDelegate*) [[UIApplication sharedApplication] delegate];
SLComposeViewController *faceBookPost = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[faceBookPost setInitialText:#"test"];
[faceBookPost addURL:[NSURL URLWithString:#"http://www.google.com"]];
[[app navController] presentModalViewController:faceBookPost animated:YES];
faceBookPost.completionHandler = ^(SLComposeViewControllerResult result)
{
[[CCDirector sharedDirector] resume];
[[CCDirector sharedDirector] startAnimation];
[[app navController] dismissModalViewControllerAnimated:YES];
dogeAmount.string = [NSString stringWithFormat:#"%.2LF", _doge];
[self setDate];
NSLog(#"Posted to Facebook");
};
} else if (elapsedTime < 24) {
NSLog(#"%f", elapsedTime);
}
}
And here is the SetDate that is called in the above:
-(void)setDate {
NSString *dateString = [NSDateFormatter localizedStringFromDate:[NSDate date]
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterFullStyle];
defaults = [NSUserDefaults standardUserDefaults];
startTime = [NSDate date];
calendar = [NSCalendar currentCalendar];
components = [calendar components:(NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:startTime];
hour = [components hour];
minute = [components minute];
NSNumber *hourNumber = [NSNumber numberWithInteger:hour];
NSNumber *minuteNumber = [NSNumber numberWithInteger:minute];
[defaults setObject:startTime forKey:#"startTime"];
[defaults setObject:hourNumber forKey:#"hour"];
[defaults setObject:minuteNumber forKey:#"minute"];
[defaults setInteger:elapsedTime forKey:#"elapsedTime"];
NSLog(#"%#",dateString);
elapsedTime = fabs([startTime timeIntervalSinceNow]);
[defaults synchronize];
}
I can't figure out what I'm doing wrong here.
.h
#property (strong, nonatomic) NSDate *dateString;
.m
- (void)share
{
double dateSinceNow = -[self.dateString timeIntervalSinceNow];
if (dateSinceNow > 24)
{
//share to facebook
}
self.dateString = [NSDate date];
}
You are constantly comparing [NSDate date] to now, which will only be the time between the instantiation and now. That is the reason it is always small.
Also, if you want to disable a button, why not disable it and schedule a method to enable it after 24 second delay?
Your fundamental problem is that you calculate elapsedTime in your setDate method, but you only call setDate once, immediately after you post Facebook, so elapsedTime is never going to change after that initial setting.
I presume that you are also storing the post date in NSUserDefaults so that you can keep track of the value across application launches? If so, where do you read the value back in? In viewDidLoad ?
I would get rid of the elapsedTime variable and create a postDate #property -
#property (strong,nonatomic) NSDate *postDate;
Then, in your viewDidLoad method -
self.postDate=[[NSUserdefaults standardUserDefaults] objectForKey:#"postDateKey"];
Now your postToFaceBook can be -
-(void)postToFacebook
{
if (self.postDate == nil || [self.postDate timeIntervalSinceNow] > 24) {
[[CCDirector sharedDirector] pause];
[[CCDirector sharedDirector] stopAnimation];
CCAppDelegate *app = (CCAppDelegate*) [[UIApplication sharedApplication] delegate];
SLComposeViewController *faceBookPost = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[faceBookPost setInitialText:#"test"];
[faceBookPost addURL:[NSURL URLWithString:#"http://www.google.com"]];
[[app navController] presentModalViewController:faceBookPost animated:YES];
faceBookPost.completionHandler = ^(SLComposeViewControllerResult result)
{
[[CCDirector sharedDirector] resume];
[[CCDirector sharedDirector] startAnimation];
[[app navController] dismissModalViewControllerAnimated:YES];
dogeAmount.string = [NSString stringWithFormat:#"%.2LF", _doge];
self.postDate=[NSDate date];
[[NSUserDefaults standardDefaults] setObject:self.postDate forKey:#"postDateKey"];
NSLog(#"Posted to Facebook");
};
}
}
The simple way to do this is to get a free timer from the performSelector:afterDelay: method on NSObject,,,
- (void)disablePosting {
// set a property that disallows posting, like a bool, or even better
// do something to the ui that disables, like disabling the button
self.postingEnabled = NO;
}
- (void)enablePosting {
self.postingEnabled = YES;
}
Then, to disable for 24 seconds, do this:
[self disablePosting];
[self performSelector:#selector(enablePosting) afterDelay:24];
And your FB code looks like this:
if (self.postingEnabled) {
// do the fb request here, without any references to timeout other than
}
Now you can take all of the other date and elapsedTime related code out.
One little gotcha, remember to cancel the perform request on dealloc
- (void)dealloc {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
Just save the date:
-(void)setStartTime {
defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSDate date] forKey:#"startTime"];
}
Later retrieve the date:
- (BOOL)isTimeExpired {
defaults = [NSUserDefaults standardUserDefaults];
NSDate *startedDate = [defaults objectForKey:#"startTime"];
NSTimeInterval elapsedTime = -[startedDate timeIntervalSinceNow];
return elapsedTime > 24;
}
Note: NSDate simply is the number of seconds since the first instant of 1 January 2001, GMT.

Updating UILabel with NSTimer stops after switching UIViewControllers

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.

imagePickerControllerDidCancel with UIAlertView not working right

I am trying to present a camera view controller with a custom overlay containing two labels. I have been unable to get my imagePickerControllerDidCancel method and alertView to work properly. The system is printing the correct information to the NSLog but the lines of code that I have written are not doing anything and when the user taps 'Back' (to cancel the alertView and return to the imagePicker) the camera appears again but the takePhoto button and Cancel button are no longer accessible. They can still be seen but not tapped. When the user clicks the button at index 1 (Leave) I want the camera to be dismissed and to take the user back to my homeViewController which is at tabBarController index:1. In addition, when the camera first loads, the timerLabel is displayed with the correct data but then quickly disappears. I feel it has something to do with my refreshLabel method but again, I do not clearly understand where I am going wrong. None of these things are working for me and I'm still very new to the programming world so help with any of these matters would be greatly appreciated. thanks!
#import "CameraViewController.h"
#import "FriendsViewController.h"
#import <mobileCoreServices/UTCoreTypes.h>
#interface CameraViewController ()
#end
#implementation CameraViewController
#synthesize titleLabel;
#synthesize timerLabel;
- (void)viewDidLoad
{ [super viewDidLoad];
self.recipients = [[NSMutableArray alloc] init];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.image == nil && [self.videoFilePath length] == 0) {
self.imagePickerController = [[UIImagePickerController alloc] init];
self.imagePickerController.delegate = self;
self.imagePickerController.allowsEditing = NO;
self.imagePickerController.videoMaximumDuration = 10;
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;}
self.imagePickerController.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:self.imagePickerController.sourceType];
[self presentViewController:self.imagePickerController animated:NO completion:nil];}
[[NSBundle mainBundle] loadNibNamed:#"OverlayView" owner:self options:nil];
self.overlayView.frame = CGRectMake(160,8,0,0);
self.imagePickerController.cameraOverlayView = self.overlayView;
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *deadline = [NSString stringWithFormat:#"%#/deadline.txt",
documentsDirectory];
NSString *name = [NSString stringWithFormat:#"%#/name.txt",
documentsDirectory];
NSError *fileError;
titleLabel.text = [NSString stringWithContentsOfFile:name
encoding:NSASCIIStringEncoding
error:&fileError];
timerLabel.text = [NSString stringWithContentsOfFile:deadline
encoding:NSASCIIStringEncoding
error:&fileError];
if(fileError.code == 0){
NSLog(#"deadline.txt was read successfully with these contents: %#,",
timerLabel.text);
NSLog(#"name.txt was read successfully with these contents: %#,",
titleLabel.text);}
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(refreshLabel)
userInfo:nil
repeats:YES];
}
-(void)refreshLabel;{
self.formatter = [NSDateFormatter new];
[self.formatter setDateFormat:#"dd:hh:mm:ss"];
NSDate *startDate = [self.formatter dateFromString:self.timerLabel.text];
NSDate *timeLeft = [startDate dateByAddingTimeInterval:-1];
NSTimeInterval totalCountdownInterval = -1;
NSTimeInterval elapsedTime = [timeLeft timeIntervalSinceNow];
NSTimeInterval remainingTime = totalCountdownInterval - elapsedTime;
if (remainingTime <= 0.0) {
//dismiss controller and set to homecontroller at tabBar index 1
}
self.timerLabel.text = [self.formatter stringFromDate:timeLeft];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Are you Sure?"
message:#"you can't come back"
delegate:self cancelButtonTitle:#"Back" otherButtonTitles:#"Yes", nil];
[alertView show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex==1) {
[self.imagePickerController dismissViewControllerAnimated:NO completion:nil];
[self.tabBarController setSelectedIndex:1];
NSLog(#"Leave clicked");
}
else {
[self reset];
NSLog(#"cancel clicked");
}
}
- (void)reset {
self.image = nil;
self.videoFilePath = nil;
}

Pausing a NSTimer/Stopwatch [duplicate]

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];
}

UILabel Not Updating When Returning to UIViewController

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.

Resources