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.