so here is the problem i have, i created two NSTimer objects and they are fired when i button is pressed. then the user has 20 seconds to press another button which forces an alert to popup where they enter a validation code, and when they press the confirm button on the alert it is supposed to stop the timer. what is happening is that everything works until they press confirm but instead of the timer stopping it hangs for a second( which im thinking is a delay caused by the keyboard dismiss animation) and then the timer continues. any help would be greatly appreciated and here is all relevant code:
#import "hackergameViewController.h"
#import <AudioToolbox/AudioToolbox.h>
#implementation hackergameViewController
#synthesize decryptLabel, crackLabel, decryptButton, crackButton, submit, numberToDecrypt, numberToCrack, stopDecryptButton, stopCrackButton, inputCode;
#synthesize soundFileURLRefBeep;
#synthesize soundFileURLRefBuzz;
#synthesize soundFileObjectBeep;
#synthesize soundFileObjectBuzz;
NSTimer *decryptTimer;
NSTimer *crackTimer;
int crackTime;
int decryptTime;
NSString *codeToConfirm;
#pragma mark UIAlertView
- (void)confirm:(UIAlertView *)confirm clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0){
[confirm dismissWithClickedButtonIndex:0 animated:YES];
}
else {
if (inputCode.text == codeToConfirm) {
[self stopCrack];
[self stopDecrypt];
}
}
}
-(void) generateDecryptionCode{
codeToConfirm = [NSString stringWithFormat:#"%i%i%i%i%i%i", arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10];
numberToDecrypt.text = codeToConfirm;
}
-(void) generateCrackCode{
codeToConfirm = [NSString stringWithFormat:#"%i%i%i%i%i%i%i%i%i%i", arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10,arc4random() % 10];
numberToCrack.text = codeToConfirm;
}
- (void)dealloc {
[decryptLabel release];
[decryptButton release];
[crackLabel release];
[crackButton release];
[submit release];
[numberToCrack release];
[numberToDecrypt release];
[super dealloc];
}
- (void) confirmCode{
UIAlertView *confirm = [[UIAlertView alloc] initWithTitle:#"Confirm Code" message:#"Please Input The Correct Code:" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Confirm Code", nil];
inputCode = [[UITextField alloc] initWithFrame:CGRectMake(12, 45, 260, 25)];
CGAffineTransform myTransform = CGAffineTransformMakeTranslation(0, 60);
[confirm setTransform:myTransform];
[inputCode setBackgroundColor:[UIColor whiteColor]];
[confirm addSubview:inputCode];
[confirm show];
[confirm release];
[inputCode release];
}
- (void) decryptTimerFires{
if(decryptTime > 0){
decryptTime--;
decryptLabel.text = [NSString stringWithFormat:#"%g", (float)decryptTime/10];
if(decryptTime%10 == 0){
AudioServicesPlaySystemSound (self.soundFileObjectBeep);
}
}
else{
[decryptTimer release];
decryptTimer = nil;
}
}
- (void) crackTimerFires{
if(crackTime > 0){
crackTime--;
crackLabel.text = [NSString stringWithFormat:#"%g", (float)crackTime/10];
if(crackTime%10 == 0){
AudioServicesPlaySystemSound (self.soundFileObjectBeep);
}
else if(crackTime == 0){
AudioServicesPlaySystemSound (self.soundFileObjectBuzz);
}
}
else{
[crackTimer release];
crackTimer = nil;
}
}
-(void) stopDecrypt{
[decryptTimer invalidate];
[decryptTimer release];
decryptTimer = nil;
}
-(void) stopCrack{
[crackTimer invalidate];
[crackTimer release];
crackTimer = nil;
}
-(IBAction)decrypt{
[self generateDecryptionCode];
decryptTime = 200;
decryptTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(decryptTimerFires) userInfo:nil repeats:YES];
[decryptTimer fire];
}
-(IBAction)crack{
[self generateCrackCode];
crackTime = 200;
crackTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(crackTimerFires) userInfo:nil repeats:YES];
[crackTimer fire];
}
In your confirm method, are you sure that your inputCode.text == codeToConfirm is returning true? Have you tried using [inputCode.text compare:codeToConfirm]? From what I can see, your timers only get invalidated in stopCrack and stopDecrypt, which only get called if that comparison succeeds.
First, you should not be calling "fire" manually unless you want the timer to fire immediately.
Second, you should call -invalidate rather than -release (as you're doing in your -decryptTimerFires and -crackTimerFires methods) when you want your timer to go away, then nil it out as you're doing. You're using the +scheduled... class method, which schedules the timer on the current run loop (which retains it, so it's not up to you to release it). -invalidate removes it from the run loop properly.
From the docs:
This method is the only way to remove
a timer from an NSRunLoop object.
Related
i am working in iPhone application, Using NSTimer to create time count to set in the screen like initially 100 then elapsed like 99,98,97 etc... if i have completed the game with available elapsed time, then i showed AlertView like successfully finished, the user press ok button navigate to previous screen, then again go to game screen, at the time ellapsed time start with previous elapsed time like 66,65,64 etc... i want, when the user go to game screen again the time count start with 100, how to fix this issue?, please help me
Thanks in Advance
I tried this:
- (void)viewDidLoad
{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(elapsedTime) userInfo:nil repeats:YES];
}
-(void)elapsedTime
{
static int i = 100;
NSLog(#"i:%d",i);
lbl.text = [NSString stringWithFormat:#"%d",i];
i--;
if(i<0)
{
[timer invalidate];
timer = nil;
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[timer invalidate];
timer = nil;
[self.navigationController popViewControllerAnimated:YES];
}
Define int i as class variable in .h file
- (void)viewDidLoad
{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(elapsedTime) userInfo:nil repeats:YES];
i = 100;
}
-(void)elapsedTime
{
i--;
if(i<0)
{
//show alertView here
[timer invalidate];
timer = nil;
}
}
Does any one want to teach me how to using loop process using NSTimer? I have 2 process timer to do, timer1 and timer2 repeated to n loop (timer1 -> timer2 -> timer1 -> timer2 and so on until n loop) that are triggered by a button. I am new to xcode. please teach me. both timer has input by user. please give me example if possible.
My code should be like this. Corret me if i'm wrong
- (void) timer2Elapsed:(NSTimer *) timer;
{
...
displayCountDown.text = [NSString stringWithFormat:#"%d : %d : %d", hour , minute, second];
}
- (void)timer1Elapsed: (NSTimer *) timer;
{
...
displayCountDown.text = [NSString stringWithFormat:#"%d : %d : %d", hour , minute, second];
}
My button who trigger the countdown:
- (IBAction)startBtn:(id)sender {
endSetTime = [NSDate timeIntervalSinceReferenceDate] + totalSecondTime;
endSetRest = [NSDate timeIntervalSinceReferenceDate] + totalSecondRest;
countdownTimer = [NSTimer scheduledTimerWithTimeInterval: 0.99 target: self selector: #selector(timer1Elapsed:) userInfo: nil repeats: YES];
}
someone here told me to using this code but i don't know what should i write inside and where to put it in? He said for looping? I don't know? And how to connect that code inside my button?
+ (void) startTimer:(id)timer
{
static int numberOfTimes = 0;
if(numberOfTimes >= 5)
{
numberOfTimes = 0;
return;
}
numberOfTimes ++;
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(timer1Elapsed:) userInfo:nil repeats:NO];
}
Forget timers. Use performSelector:withObject:afterDelay: selector.
#interface CECountdown : NSObject {
NSDate *_startDate;
NSTimeInterval _countdownInterval;
}
#end
#implementation CECountdown
- (id)initWithCountdownInterval:(NSTimeInterval)interval {
self = [super init];
if(self) {
_countdownInterval = interval;
_startDate = [[NSDate date] retain];
}
return self;
}
- (void)dealloc {
[_startDate release];
[super dealloc];
}
- (void)method0 {
// Do something
[self performSelector:#selector(method1) withObject:nil afterDelay:0.3];
[self checkTimeout];
}
- (void)method1 {
// Do something
[self performSelector:#selector(method0) withObject:nil afterDelay:0.3];
[self checkTimeout];
}
- (void)invalidate {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(method1) object:nil];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(method2) object:nil];
}
- (void)checkTimeout {
if([NSDate timeIntervalSinceReferenceDate] - [_startDate timeIntervalSinceReferenceDate] > _countdownInterval) {
[self invalidate];
}
}
- (void)didTapStartButton:(id)sender {
[self performSelector:#selector(method1) withObject:nil afterDelay:0.3];
}
#end
The following method causes a crash. The UI is like a button, which handles the start / stop functionality of the NSTimer. If the timer runs, a UILabel is updated. Using the viewDidLoad Method makes my timer work, stopping it works too, but starting it again crashes the app.
Removing the alloc in the viewDidLoad method and trying to use the start button causes a crash instantly. Even the NSLog(#"Start now");is not called.
Code:
- (void)tick {
NSLog(#"tick");
float value = [moneyLabel.text floatValue];
moneyLabel.text = [NSString stringWithFormat:#"%f", value + 1.0];
}
- (IBAction)startStopButtonClicked:(UIButton *)sender {
if ([sender.titleLabel.text isEqualToString:#"Start"]) {
NSLog(#"Start now");
if (timer) {
NSLog(#"Timer valid");
[timer fire];
} else {
NSLog(#"Timer is nil");
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(tick) userInfo:nil repeats:YES];
[timer fire];
}
NSLog(#"bla");
[sender setTitle:#"Stop" forState:UIControlStateNormal];
} else {
[timer invalidate];
timer = nil;
NSLog(#"Stopped.");
NSLog(#"Timer isValid: %#", timer);
[sender setTitle:#"Start" forState:UIControlStateNormal];
}
}
I don't see the need to call [NSTimer fire] at all; it should be enough to allow the timer to decide when to fire.
Firstly ensure that timer is nil (it should be if it's an instance variable of the object), although explicitly setting it to nil in - (id)init won't hurt.
Next I would use the state of the timer itself to determine whether start/stop has been pressed, not the text in the button:
- (IBAction)startStopButtonClicked:(UIButton *)sender
{
if (timer != nil)
{
NSLog(#"Stopping timer");
[timer invalidate];
timer = nil;
}
else
{
NSLog(#"Starting timer");
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
}
[sender setTitle:(timer != nil ? #"Stop" : #"Start")
forState:UIControlStateNormal];
}
The code you have posted works as desired - just tested it in a new project, so the problem could be somewhere else. I tested it only by declaring the ivar NSTimer *timer; without any initialization in viewDidLoad: or the designated initializers...
I am using a sub class in my application, in which i am using nstimer to detect user idle/inactivity.
Here is a leak and it raised on every tap.
here is code of my class
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
isRootView=FALSE;
// Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets.
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0) {
// allTouches count only ever seems to be 1, so anyObject works here.
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan)
[self resetIdleTimer];
}
}
- (void)resetIdleTimer {
if (idleTimer)
{
if ([idleTimer isValid])
{
[idleTimer invalidate];
//[idleTimer release];
//idleTimer=nil;
}
}
maxIdleTime = 60;
if (!isRootView)
{
idleTimer = [NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
[idleTimer retain];
}
else {
if ([idleTimer isValid])
{
[idleTimer invalidate];
//[idleTimer release];
//idleTimer = nil;
}
if ([resetTimer isValid]) {
[resetTimer invalidate];
resetTimer=nil;
}
}
}
- (void)idleTimerExceeded {
alert=[[UIAlertView alloc] initWithTitle:#"Confirmation!" message:#"Would you like to continue placing the order ?" delegate:self cancelButtonTitle:#"NO" otherButtonTitles:#"YES", nil];
alert.tag=100;
[alert show];
[alert release];
resetTimer=[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:#selector(resetApplication) userInfo:nil repeats:NO] ;
}
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (alertView.tag==100 ) {
if (buttonIndex==1)
{
if ([resetTimer isValid]) {
[resetTimer invalidate];
resetTimer=nil;
}
}
else {
[self resetApplication];
}
}
}
-(void) resetApplication
{
isRootView=TRUE;
[alert dismissWithClickedButtonIndex:1 animated:YES];
if ([resetTimer isValid])
{
[resetTimer invalidate];
resetTimer=nil;
}
if (idleTimer)
{
[idleTimer invalidate];
[idleTimer release];
idleTimer = nil;
}
SushiTeriaAppDelegate *appDelegate=(SushiTeriaAppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate resetApp];
}
- (void)dealloc {
[super dealloc];
//[resetTimer release];
[alert release];
}
I have retain this timer. If dont retain then application get crashed.
Please guide me how to remove this leak
shivam
Use assigned property for timer variable.
ex.
NSTimer globalTimer;
#property (nonatomic, assigned) NSTimer globalTimer;
#synthesize globalTimer;
Now assign timer on this variable and use like self.globalTimer.
One thing is that always assign nil on variable after use. Do not release it another wise it will
crashed your application.
What is happening is, you are calling the resetIdleTimer method every time the user taps on the screen. This increases the retain count of the idleTimer every time because you have retained it. The reason your app crashes if you don't retain is because you get an autoreleased NSTimer object from NSTimer class method
idleTimer=[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
This means that you cannot use this object outside that particular method as you do in your resetApplication method. Now you could check the retain count every time you retain it but that is a hassle. So what I suggest is you declare idleTimer as a retain property.
#property (retain) NSTimer *idleTimer;
and synthesize its setters and getters after implementation.
#synthesize idleTimer;
now use
self.idleTimer=[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
and release idleTimer only in your dealloc.
I have been having problems creating a UISlider that can be used to modify a NSTimer I created. Essentially the slider is ment to modify the integer that the NSTimer is counting down from, but when I try to move the UISlider, the application crashes, I'm assuming this is because it is interfering with the countdown that is occurring, but I don't know what to do to fix this.
Here is the relevant code
- (void)viewDidLoad {
[label setFont:[UIFont fontWithName:#"DBLCDTempBlack" size:36.0]];
label.text = #"I0A0IN6";
mainInt = mySlider.value;
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/1.0) target:self selector:#selector (timerVoid) userInfo:nil repeats:YES];
[super viewDidLoad];
}
- (void)timerVoid {
mainInt += -1;
if (mainInt == 0) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Break Time!"
message:#"Time to take a break, please go to the exorcises page during your break inorder to maximise it"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
[mainInt invalidate];
}
[label setFont:[UIFont fontWithName:#"DBLCDTempBlack" size:36.0]];
label.text=[NSString stringWithFormat:#"%d" , mainInt];
}
The slider is called mySlider, and it is modifying the interger "mainInt" (line 5).
Few things:
[super viewDidLoad]; line is better be first in viewDidLoad.
There is no need to set the font each time timerVoid is executed.
As I have mentioned in the comment, you should call invalidate on timer and not on mainInt.
The slider does not modify mainInt - you have set the value of mainInt to hold the initial value of your slider and it is not changed by itself when you change the value of the slider. In order to do that you should create an IBAction and connect it to slider's valueChanged event. Inside that IBAction you may do what ever you want with the new value of the slider - for example set the value of mainInt or reschedule your timer.
You may also avoid using the IBAction by using the mySlider.value directly everywhere you need.
I don't see any reason for crashing the app though...
Possible code:
- (void)viewDidLoad {
// This line should be first
[super viewDidLoad];
[label setFont:[UIFont fontWithName:#"DBLCDTempBlack" size:36.0]];
label.text = #"I0A0IN6";
// There is no need in mainInt
//mainInt = mySlider.value;
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerVoid) userInfo:nil repeats:YES];
}
- (void)timerVoid {
// This line is much more readable like this ("-=" instead of "+= -")
mySlider.value -= 1;
// Much better to use "<=" in this case to avoid bugs
if (mySlider.value <= 0) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Break Time!"
message:#"Time to take a break, please go to the exorcises page during your break inorder to maximise it"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
[alert release]; // Haven't noticed the lack of release here earlier...
// timer and not mainInt
[timer invalidate];
}
// The next line is unnecessary
//[label setFont:[UIFont fontWithName:#"DBLCDTempBlack" size:36.0]];
label.text = [NSString stringWithFormat:#"%d", mySlider.value];
}
EDIT:
Added the [alert release]; line
Your app is crashing because you haven't linked the valueChanged event from your slider to any proper IBAction method.