I have a timer in a UIViewController:
#interface MyViewController : UIViewController <UITableViewDataSource,UITableViewDelegate, MFMailComposeViewControllerDelegate>
...
#property (nonatomic, assign) int m_remaining_time;
...
#end
In .m file:
#interface MyViewController ()
{
NSTimer *m_timer;
}
The timer starts on viewDidAppear()
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
The method decrementSpin simply decrements the counter and prints a log:
- (void)decrementSpin
{
self.m_remaining_time--;
if(self.m_remaining_time > 0) {
NSLog(#"[TIMER] >> %d", self.m_remaining_time);
}
else {
NSLog(#"[TIMER] >> Time finished!");
[m_timer invalidate];
m_timer = nil;
}
}
I destroy the timer in every case the ViewController will close:
In appDidEnterInBackground, viewDidDisappear, when a button that closes the view has been pushed (goind to next view for ex)
The problem is that, in some cases, I have seen in the simulator the log [TIMER] >> %d that keeps printing even after I closed the view.
It is not always the case, but sometimes it keeps counting.
Any clues ?
Edit:
#skaak, I do the following code in appDidEnterInBackground, viewDidDisappear:
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
Then when the player pushes the "Next" and "Home" buttons.
For example the case of "Home" button:
-(IBAction)homeButtonPressed:(id)sender{
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
InitScreenViewController* initVc = [self.storyboard instantiateViewControllerWithIdentifier:#"initScreenViewController"];
UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:initVc];
[navVC setNavigationBarHidden:true];
[UIView transitionWithView:[AppDelegate getShareInstance].window duration:0.1f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[AppDelegate getShareInstance].window.rootViewController = navVC;
}completion:nil];
}
Edit #2:
I added the following flag:
#property (assign, nonatomic) BOOL isCurrentlyViewShowed;
Which is set on viewDidAppear (along with your suggestion):
self.isCurrentlyViewShowed = TRUE;
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
Then, it is set to off everytime the view is closed:
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
self.isCurrentlyViewShowed = FALSE;
And finally, on decrementSpin:
if(self.isCurrentlyViewShowed == FALSE){
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
}
self.m_remaining_time--;
if(self.m_remaining_time > 0) {
NSLog(#"[TIMER] (%p)>> %d (FLAG: %d)", m_timer, self.m_remaining_time, self.isCurrentlyViewShowed);
}
else {
NSLog(#"[TIMER] (%p)>> Time finished (FLAG: %d)", m_timer, self.isCurrentlyViewShowes);
[m_timer invalidate];
m_timer = nil;
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
I did repeated the test like 100 times and I managed to reproduce it once!
I left this view, and I saw on the log the line:
[TIMER] (0x2818ecfc0)>> 58 (FLAG: 1)
[TIMER] (0x2818ecfc0)>> 57 (FLAG: 1)
...
It means that somehow the View object is re-initialized ? I don't get it..
This is tricky, and need the whole code to try and isolate it. But here is an idea that might help. In fact, I think you need to do it like this anyhow. Change your viewDidAppear to something like below.
- viewDidAppear
{
if ( ! m_timer )
{
m_timer = ...
}
}
or, depending on the logic, to
- viewDidAppear
{
if ( m_timer )
{
[m_timer invalidate];
m_timer = nil;
}
m_timer = ...
}
In either case you will be sure you have only one timer running at a time which may be your problem.
Related
I want to show user that the value initially is 0 than it increase to 1 than 2 than 3 sequence repeat after some seconds
Worked done by me
I write this code in viewdidload but it print 99 in label directly but i want to show user changing values from 0 to 99.
for(int i=0;i<100;i++){
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
}
I think this will help you:
#interface ViewController (){
int i;
NSTimer *myTimer;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(setData)
userInfo:nil
repeats:YES];
i = 0;
}
-(void)setData{
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
i = i + 1;
if(i == 100){
[myTimer invalidate];
myTimer = nil;
}
}
Based on your requirement you need to use NSTimer for update your label progress
Step 1: Create 2 variable in your #interface class
#interface BUViewController: UIViewController {
NSTimer *timerCount;
int progress;
}
Step 2: Start Timer in your viewDidLoad() with progress value 0.
progress = 0;
timerCount = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
Step 3: create method with timer update label text an check progress value in it and stop timer while its reach 100.
-(void)updateLable{
if (progress<100) {
lblProgress.text = [NSString stringWithFormat:#"%d",progress];
progress++;
}else{
[timerCount invalidate];
timerCount = nil;
}
}
Hope this will help you to achieve your expected output.
For this you can use NSTimer you need to update UILabel value in its selector method. You can use this code.
-(void)viewDidLoad{
[super viewDidLoad];
self.time = 0;
_labelTimer.text =#"0";
[self startTimer];
}
- (void)startTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)timerAction:(NSTimer *)timer {
self.time++;
if (self.time == 100)
{
[self stopTimer];
}
_labelTimer.text = [NSString stringWithFormat:#"%d",self.time];
}
-(void)stopTimer{
[self.timer invalidate];
self.timer = nil;
}
Here is code for your problem .hope it helps you.
- (void)viewDidLoad
{
[super viewDidLoad];
counter = 0;
t = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
}
-(void)updateLabel
{
for (int i = counter; i<counter+1; i++)
{
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
}
counter++;
if (counter ==101)
{
[t invalidate];
}
}
You are blocking run loop with your cycle. You just need resume it in each iteration.
for(int i=0;i<100;i++){
_totalEffort.text=#(i).stringValue;
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
I have an endGame view that loads when the user completes a game. It is presented modally over top of the game view. In viewDidLoad I am calling a method to save data such the score and how many of a certain game mode they have played. That method is being called multiple times - about once per second, but it is irregular.
End Game Screen:
#import "endGameScreen.h"
#import "ViewController.h"
#import "GameData.h"
#implementation endGameScreen
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)awakeFromNib {
[self getAchievementData];
}
- (void)getAchievementData {
[RWGameData sharedGameData].timedPlayed++;
NSLog(#"Timed played: %ld", [RWGameData sharedGameData].timedPlayed);
}
- (void)writeAchievementforKey:(NSString *)achKey {
[data setObject:[NSNumber numberWithInteger:1] forKey:achKey];
[data writeToFile: path atomically:YES];
}
I am loading the endGameScreen class like this:
GameViewController.m
- (void)viewDidLoad {
if (self.gameMode == 1) {
self.gridSize = 3;
secondsLeft = 10;
[self countdownTimer];
}
}
-(void)countdownTimer {
secondsLeft = minutes = seconds = 0;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:) userInfo:nil repeats:YES];
}
// Timer in the game going to zero
- (void)updateCounter:(NSTimer *)theTimer {
if (secondsLeft > 0 ){
secondsLeft -- ;
minutes = (secondsLeft % 3600) / 60;
seconds = (secondsLeft %3600) % 60;
timePlayedLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutes, seconds];
if (secondsLeft == 1) {
//Call endGame method
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(endGame)
userInfo:nil
repeats:NO];
}
}
else {
secondsLeft = 10;
}
}
- (void)endGame {
[timer invalidate];
[self saveScores];
//Segue in the storyboard
[self performSegueWithIdentifier:#"endGame" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"endGame"]) {
endGameScreen *vc = [segue destinationViewController];
if (self.gameMode == 1) {
vc.gameMode = 1;
}
else {
vc.gameMode = 2;
}
}
}
I am getting NSLog messages that continually increment saying "Mode One played: 27", "Mode One played: 28", "Mode One played: 29", and so on.
Your -endGame method isn't invalidating the timer you think it is.
-updateCounter: is overwriting your timer instance variable with the new one-shot timer for calling -endGame, but that doesn't stop the other timer, the one created in -countdownTimer. That timer continues to fire, calling -updateCounter:, which keeps creating more one-shot timers to call -endGame. So, that gets called repeatedly.
I want to create a button that takes me to another view controller and automatically starts a countdown from 3 to 0, but i don't know how to set the countdown on the other view controller. Here's the code i tried:
#implementation TestViewController
-(IBAction)test:(id)sender {
CountdownViewController *cdvc = [[CountdownViewController alloc]
initWithNibName:#"CountViewController" bundle:nil];
[self.navigationController pushViewController:cdvc animated:YES];
}
#implementation CountdownViewController
int maintInt = 3;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countDown) userInfo:nil repeats:YES];
-(void)countDown {
maintInt -= 1;
count.text = [NSString stringWithFormat:#"%i", maintInt];
if(maintInt==1){
[timer invalidate];
}
}
You have to place your countdown code in a method, which you should call in viewDidLoad or viewWillAppear method regarding your needs.
#implementation TestViewController
-(IBAction)test:(id)sender {
CountdownViewController *cdvc = [[CountdownViewController alloc]
initWithNibName:#"CountViewController" bundle:nil];
[self.navigationController pushViewController:cdvc animated:YES];
}
#end
#implementation CountdownViewController
- (void)viewDidLoad {
[super viewDidLoad];
// .. customize your views here ..
// Initialize the timer once your view has been loaded.
//
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countDown:) userInfo:nil repeats:YES];
}
static int maintInt = 3;
- (void)countDown:(NSTimer *)timer {
count.text = [NSString stringWithFormat:#"%i", maintInt];
maintInt -= 1;
if (maintInt == 0 && timer) {
[timer invalidate];
timer = nil;
}
}
#end
#interface Taxi_MainViewController : Taxi_BaseNavViewController
{
__block NSTimer *timer_;
}
[Taxi_do_order psg_place_orderWithMemberId:strMemberId_ orderStatus:K_orderStatus_open andCLLocationCoord:location.coordinate callback:^(NSInteger iOrderId){
[Taxi_StatusView dismiss];
if (iOrderId >=0)
{
isOrderExist = YES;
[weakSelf_ showWaittingDriverAcceptView];
timer_ = [NSTimer scheduledTimerWithTimeInterval:2.0
target:weakSelf_
selector:#selector(actListen:)
userInfo:nil
repeats:YES];
}else
[weakSelf_ hideWaittingDriverAcceptView];
}faile:^(){
[Taxi_StatusView showLostNetWork];
}];
when i call [timer_ invalidate],timer_ = nil; at other method, the timer function still call every 2 second.
In your case, the variable timer_ is never keep, and you lose the pointer
at the end block scope. With a property you have retain / release mechanism.
Try this :
#interface Taxi_MainViewController : Taxi_BaseNavViewController
{
}
#property (nonatomic,retain) NSTimer* timer;
#end
[Taxi_do_order psg_place_orderWithMemberId:strMemberId_ orderStatus:K_orderStatus_open andCLLocationCoord:location.coordinate callback:^(NSInteger iOrderId){
[Taxi_StatusView dismiss];
if (iOrderId >=0)
{
isOrderExist = YES;
[weakSelf_ showWaittingDriverAcceptView];
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:weakSelf_
selector:#selector(actListen:)
userInfo:nil
repeats:YES];
}else
[weakSelf_ hideWaittingDriverAcceptView];
}faile:^(){
[Taxi_StatusView showLostNetWork];
}];
Sorry this is a repeat question, but I have tried all the solutions I could find without success.
Anyway, here is my problem, specifically. I have the following code where I start a timer on a long press gesture, then stop it when that gesture is cancelled or when the action is complete.
-(void)photoLongPress:(UILongPressGestureRecognizer *)press
{
NSTimer *timer = nil;
if (press.state == UIGestureRecognizerStateBegan)
{
//Start timer
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(photoLongPressTimer:) userInfo:nil repeats:YES];
}
if (press.state == UIGestureRecognizerStateEnded)
{
[timer invalidate];
timer = nil;
}
}
-(void)photoLongPressTimer:(NSTimer *)timer
{
DAProgressOverlayView *progress = (DAProgressOverlayView *)[self.view viewWithTag:kTagPhotoDeleteProgressIndicator];
progress.progress += 0.08;
if (progress.progress == 1)
{
[self deletePhotoSelection];
[timer invalidate];
timer = nil;
}
}
The timer stops when invalidated in the photoLongPressTimer method, but not if the gesture stops in the photoLongPress method. Why might this be? I'm stumped.
Thanks.
You need to store timer in an instance variable so that you can refer to it later. It works in photoLongPressTimer: because the timer is passed as a parameter but in photoLongPress: it is nil because you haven't just created it and it's a local reference.
Add a property:
#property (strong, nonatomic) NSTimer *timer;
Store the new timer into it:
self.timer = [NSTimer schedu...
Invalidate and nil the property:
[self.timer invalidate];
self.timer = nil;
NSTimer *timer = nil;
This line only creates a local variable.
You must make a property for the timer.
Put this in your .h file.
#property (strong) NSTimer *timer;
Then use the code here
-(void)photoLongPress:(UILongPressGestureRecognizer *)press
{
if (press.state == UIGestureRecognizerStateBegan)
{
//Start timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(photoLongPressTimer:) userInfo:nil repeats:YES];
}
if (press.state == UIGestureRecognizerStateEnded)
{
[self.timer invalidate];
self.timer = nil;
}
}
-(void)photoLongPressTimer:(NSTimer *)timer
{
DAProgressOverlayView *progress = (DAProgressOverlayView *)[self.view viewWithTag:kTagPhotoDeleteProgressIndicator];
progress.progress += 0.08;
if (progress.progress == 1)
{
[self deletePhotoSelection];
[timer invalidate];
timer = nil;
}
}
Problem is with NSTimer *timer = nil;
in -(void)photoLongPress:(UILongPressGestureRecognizer *)press
method.
remove NSTimer *timer = nil; from this method and store instance of NSTimer.