NSTimer does not count down in while loop - ios

int maximumMinute;
NSTimer *timer;
- (void)startTimer: (int) minute {
maximumMinute = minute;
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(countDown) userInfo:nil repeats:YES];
}
- (void)countDown {
maximumMinute -= 1;
self.timerLabel.text = [NSString stringWithFormat:#"%i", maximumMinute];
if (maximumMinute == 0) {
[timer invalidate];
}
}
This is my timer code and i just start the timer in viewDidLoad
i have this method called when some view is clicked for just testing.
-(void)clickSomthing: (UITapGestureRecognizer *)recognizer {
int i = 0;
while ([timer isValid])
{
NSLog(#"timer valid");
}
Firstly, when the view is loaded, the timer count is fired and i can see the time count down actually but when i click any view associated with the method "clickSomething" the timer does not count down actually it stops.
What i really want to do is that i want the timer keep going on until it reaches zero regardless of while or for loop. Actually, I am trying to stop my game based on the timer but the game does need some while loop and for loop that would take a while.

Your code lock the main thread (you never should do this) you should use GCD - Grand Central Dispatch to let the countdown in background while the user keep using the app.
-(void)clickSomthing: (UITapGestureRecognizer *)recognizer {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
int countDown = 10; // This is the countdown time
BOOL letTheCountDown = TRUE;
while (letTheCountDown) {
[NSThread sleepForTimeInterval:1.0]; // Count second by second
countDown--;
NSLog(#"countDown %i",countDown);
if (countDown == 0) {
letTheCountDown = FALSE;
dispatch_async(dispatch_get_main_queue(), ^{
// Do something in main thread here. Ex: alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Time is Over" delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil];
[alert show];
});
}
}
});
}

Related

iOS UISwitch keep sending message every 4 second

I have a chatting app that has a uiswitch. If the Switch button is on, I want the app to send "hi" continuously every 3 seconds even the app is in background mode. I know that I can use NSTimer, but I don't really know how to implement that in this code(This is my first time to develop iOS app). Please help me with this.
My code is :
// Allocate, initialize, and add the automatic button.
_AutomaticSend = [[UISwitch alloc] initWithFrame:CGRectMake([self width] - 50.0, textAreaY + Center(160.0, textAreaHeight), 50.0, 50.0)];
[_AutomaticSend addTarget:self action:#selector(changeSwitch:) forControlEvents:UIControlEventValueChanged];
and
// Switch action
//NSUInteger counter = 0;
- (void)changeSwitch:(id)sender{
if([sender isOn]){
for (int a=1; a<=300; a++)
{
//[self performSelector:#selector(changeSwitch:) withObject:_textField afterDelay:70.0];
[sender setOn:YES animated:YES];
[_textField setText:#"hi"];
NSString * text = [_textField text];
// Update status.
[[TSNAppContext singleton] updateStatus:text];
// Add the status to the bubble.
[self appendLocalPeerTableViewCellWithMessage:text];
}
// NSLog(#"Switch is ON");
} else{
NSLog(#"Switch is OFF");
}
}
Now, the app is showing all "hi" after all 300 "hi" is ready to show. But I want it to send it one by one continuously.
Define a NSTimer instance and a counter in your view controller:
#property (nonatomic, strong) NSTimer *timer;
#property (nonatomic, assign) NSInteger counter;
Implement the method when timer is fired:
- (void)timerAction:(id)sender {
[_textField setText:#"hi"];
NSString * text = [_textField text];
// Update status.
[[TSNAppContext singleton] updateStatus:text];
// Add the status to the bubble.
[self appendLocalPeerTableViewCellWithMessage:text];
// stop the timer after sending for 300 times
self.counter += 1;
if (self.counter >= 300) {
[self.timer invalidate];
self.timer = nil;
}
}
Starts the timer when switch is on:
- (void)changeSwitch:(id)sender{
if ([sender isOn]) {
self.counter = 0;
self.timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
} else {
// kill the timer when switch is off
[self.timer invalidate];
self.timer = nil;
}
}
However, you can't make the timer to continue working indefinitely after your app is entering background (with some exceptions: VoIP, GPS applications, etc.). Please refer to the official document Background Execution.
//Nstimer *timer in .h
In your UISwitch Method Write like this
- (void)changeSwitch:(id)sender{
if([sender isOn]){
timer=[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(targetMethod)
userInfo:nil
repeats:NO];
}
else
{
[timer invalidate];
}
}
targetMethod Like this
-(void)targetMethod
{
[sender setOn:YES animated:YES];
[_textField setText:#"hi"];
NSString * text = [_textField text];
// Update status.
[[TSNAppContext singleton] updateStatus:text];
// Add the status to the bubble.
[self appendLocalPeerTableViewCellWithMessage:text];
}
Use NSTimer: Documentation of NSTimer
Declare property of NSTimer in .h file:
#property (retain,nonatomic) NSTimer *myTimer;
- (void)changeSwitch:(id)sender
{
if([sender isOn]){
myTimer = [NSTimer scheduledTimerWithTimeInterval: 4 target: self
selector: #selector(AddMessage:) userInfo: nil repeats: YES];
} else{
[self.timer invalidate];
}
}
After each 4 second, timer will call the below function:
-(void) AddMessage:(NSTimer*) timer
{
//Add Message
}

Method being called constantly until the view is dismissed

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.

iOS: Counting upwards animation

I want to animate my UILabel such that it looks like its counting upward. For arguments sake lets just say I want it to go up by 1 every second.
None of the ways below worked properly.
A simple for loop (example here) doesn't work because its way too fast.
for(int i =0;i<1000;i++)
{
lblNum.text = [NSString stringWithFormat:#"%d",i];
}
Adding in sleep(1) doesn't work because the executions are asynchronous (I think thats why at least)
I also tried:
for(int i=0;i<1000;i++)
{
[self performSelector:#selector(updateLbl:)
withObject:[NSNumber numberWithInt:i ] afterDelay:1];
}
-(void)updateLbl:(NSNumber *)num
{
lblNum.text = [NSString stringWithFormat:#"%#",num];
}
As well as:
for(int i=0;i<1000;i++)
{
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
sleep(1);
dispatch_async(dispatch_get_main_queue(), ^{
lblNum.text = [NSString stringWithFormat:#"%d",i];
});
});
}
NSTimer *timer = [NSTimer scheduleTimerWithTimeInterval:1.0 target:self selector:#selector(increment:) userInfo:label repeats:YES];
...
- (void)increment:(NSTimer *)timer {
UILabel *label = (UILabel *)timer.userInfo;
NSInteger i = label.text.integerValue;
i++;
label.text = [NSString stringWithFormat:#"%d", i];
if(someCondition){
[timer invalidate]//stops calling this method
}
}
I think a good way to do this is to use an NSTimer.
How do I use NSTimer?
The implementation is simple, just set repeat to YES and make the timer fire every second. You could have a variable keep track of count and increase it each time.
A good rule of thumb for programming: Never use sleep!!
Use NSTimer & NSRunLoop to perform animation in your code
timer_=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(labelAnimation:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer_ forMode:NSDefaultRunLoopMode];
- (void)increment:(NSTimer *)timer
{
if(isAnimationComplete)
{
[timer_ invalidate]//stops calling this method
}
else
{
//perform your action
}
}

How to make a button appear and disappear throughout a timer

I have this app which has a timer in it and I want to be able to display a button at random points in that timer. Sort Of like a game setting where you have to get the button to get more time before it disappears. I already have some code , but the problem is that the timer that i already have is being messed up ( i can tell because it is linked to a label. It messed up because it goes down by 2 instead of 1 and this only happens when i have the code to make the button appear and disappear with the timer.
Here is the code to my timer that is being messed up:
-(IBAction)Ready:(id)sender {
[self performSelector:#selector(TimerDelay) withObject:nil afterDelay:1.0];
[self performSelector:#selector(randomTimer) withObject:nil afterDelay:0.5];
}
-(void)TimerDelay {
MainInt = 36;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countDownDuration)
userInfo:nil
repeats:YES];
}
Here is the code to the button:
-(IBAction)Ready:(id)sender { //the same ready action as above
[self performSelector:#selector(TimerDelay) withObject:nil afterDelay:1.0];
[self performSelector:#selector(randomTimer) withObject:nil afterDelay:0.5];
}
-(void)TimerDelay {
MainInt = 36;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countDownDuration)
userInfo:nil
repeats:YES];
}
- (void)randomTimer {
NSInteger randomTime = arc4random_uniform(0); //Some number between 0 and 9
timer = [NSTimer scheduledTimerWithTimeInterval:randomTime
target:self
selector:#selector(showButton)
userInfo:nil
repeats:NO];
}
- (void)showButton {
if(self.plus5.hidden == YES) {
self.plus5.hidden = NO;
}
else {
self.plus5.hidden = YES;
}
[self TimerDelay]; //Call random timer function again to run this method at a future random time
}
You are calling "randomTimer" and "TimerDelay" (this should be "timerDelay") one after the other and scheduling and REscheduling "timer".
I think you should reconsider your architecture, here...
EDIT: Use just one timer and let it manage your button.
- (void)somewhereInYourCode
{
shouldShowButton = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timeFired)
userInfo:nil
repeats:YES];
}
- (void)timeFired
{
// Here your countdown and...
if (shouldShowButton) {
NSInteger randomTimeInt = arc4random_uniform(9); // as suggested by Bergasms
float randomTimeFloat = randomTimeInt;
[self performSelector:#selector(showButton) withObject:nil afterDelay:randomTimeFloat];
shouldShowButton = NO;
}
}
This way your timer won't (probably) mess up.
This:
arc4random_uniform() will return a uniformly distributed random number
less than upper_bound.
is one of your problems.
NSInteger randomTime = arc4random_uniform(0); //Some number between 0 and 9
needs to be
NSInteger randomTime = arc4random_uniform(9); //Some number between 0 and 9
I cannot say for the rest of your code. But the line you have will always return 0, which means the random timer will always execute instantly.

Stop and Reset NSTimer

I have a simple timer using that is activated with a button is pushed. It runs from 60 to 0 no problem but what I want is to stop and reset the timer on push button. I have managed to stop it when the button is pressed using the code below but for some reason cannot get it to reset and stop at 60. This should be simple but it isn't working. Any suggestions?
Timer is set using simple Action
- (IBAction)timerStart:(id)sender {
if(!secondCounter == 0){
[countdownTimer invalidate];
}
else {
[self setTimer];
}
}
CODE FOR TIMER
- (void)timerRun {
secondCounter = secondCounter - 1;
int minutes = secondCounter;
NSString *timerOutput = [NSString stringWithFormat:#"%d", minutes ];
countDownLabel.text = timerOutput;
if(secondCounter == 0){
[countdownTimer invalidate];
countdownTimer = nil;
}
}
- (void)setTimer {
secondCounter = 60;
countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerRun) userInfo:nil repeats:YES];
}
You need to set the seconds down to 0, otherwise the you always invalidate your timer, but never start it again:
- (IBAction)timerStart:(id)sender {
if(!secondCounter == 0) {
[countdownTimer invalidate];
secondCounter = 0;
}
else {
[self setTimer];
}
}
What do you mean to stop the timer? Timer is not going to stop. You would like to have to have countDownLabel.text to show 60? When you invalidate the timer it will stop running and calling timerRun method If you want to restart the timer just call setTimer function again. I think what you meant is to have countDownLabel.text = #"60" after [countdownTimer invalidate]; and when you click the button again the timer will start again and will change the label. Let me know if this is what you were looking for.
As Christian said you have to reset the secondCounter
secondCounter = 0;

Resources