My code is given below.I am trying to show a counter on the screen in a window and enable a button after count limit is over.
When I run the code before nstimer and after nstimer are printed only once and nothing is printed from the time countdown function.Someone pelase help me.
- (void)startTimer:(NSInteger)count;
{
NSLog(#"Entered the start timer function");
self.countLimit = count;
[self.lbCount setStringValue:[NSString stringWithFormat:#"%ld",self.countLimit]];
NSLog(#"Before nstimer");
#try{
self.timeCounter = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timeCountDown) userInfo:nil repeats:YES];
}
#catch (NSException* e) {
NSLog(#"%#",[e description]);
}
NSLog(#"After nstimer");
}
- (void)timeCountDown
{
NSlog(#"Entered the function");
self.countLimit = self.countLimit - 1;
NSLog(#"Just entered the function time countdown");
if (self.countLimit < 0) {
NSLog(#"Entered count limit less than 0");
[self.timeCounter invalidate];
self.timeCounter = nil;
[self.btContinue setEnabled:YES];
return;
}
[self.lbCount setStringValue:[NSString stringWithFormat:#"%ld",self.countLimit]];
}
As detailed in the API docs, your scheduled method must take a NSTimer * parameter:
- (void)timeCountDown:(NSTimer *)timer
Don't forget to change your selector in the scheduledTimerWithTimeInterval call to reflect this:
// There's a colon after selector name to denote
// the method takes one parameter
self.timeCounter = [NSTimer scheduledTimerWithTimeInterval:1
target:self selector:#selector(timeCountDown:) userInfo:nil repeats:YES];
If you don't want to pass as a arguement NSTimer* then use below after your timer
[self.timeCounter fire];
Related
I'm trying to make an NSButton (but I could use the technique for iOS too, so any answers are welcome).
I have a button where hiding/enabling is turned on and off:
-(void)textDidChange:(NSNotification *)notification
{
timerStatus = 0;
timerTest = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(changeButtonState:)
userInfo:nil
repeats:YES];
}
-(void) changeButtonState:(id) sender {
NSLog(#"%s", __FUNCTION__);
if (timerStatus == 2) return;
if (timerStatus == 0) {
timerStatus = 1;
saveButton.enabled = YES;
saveButton.hidden = NO;
} else {
timerStatus = 0;
saveButton.enabled = NO;
saveButton.hidden = YES;
}
}
The button blinks ok, but after it's associated method is fired (in this case a save action), I want the timer to stop and the button to stop blinking. It's this last part that's giving me a headache.. Any help appreciated.
- (IBAction)saveItemNotes:(id)sender
{
NSLog(#"%s", __FUNCTION__);
<do my save stuff here>
timerStatus = 2;
[timerTest invalidate];
timerTest = nil;
}
Just invalidate your timer in that same method where you change the button state.
Better yet, don't do it this way.
Use Core Animation instead if you want to draw attention to an interface item.
I'm creating a memory game and want to change the images of four buttons each iteration in a for-loop. What actually happens is that nothing happens until the last iteration of the loop.
When the button is pressed:
- (IBAction)bNewRound:(id)sender {
for (int i = 0; i < 10; i++) {
numbers1[i] = arc4random()%4+1;
NSLog(#"%i: %i", i, numbers1[i]);
if (numbers1[i] == 1) {
[self b1On];
}
if (numbers1[i] == 2) {
[self b2On];
}
if (numbers1[i] == 3) {
[self b3On];
}
if (numbers1[i] == 4) {
[self b4On];
}
sleep(1);
}
}
The method for changing the images of the buttons:
- (void)b1On {
NSLog(#"b1 state1");
[self.button1 setBackgroundImage:[UIImage imageNamed:#"memory_green.png"] forState: UIControlStateNormal];
bool1 = FALSE;
[self b2Off];
[self b3Off];
[self b4Off];
}
- (void)b1Off {
// NSLog(#"b1 state2");
[self.button1 setBackgroundImage:[UIImage imageNamed:#"memory_grey.png"] forState: UIControlStateNormal];
bool1 = TRUE;
}
(only for button 1 but you get it)
I follow the code in the log and everything works fine, every second a new number is generated and saved in the array, the b1On-method (or b2On, b3On etc) runs but the line
[self.button1 setBackgroundImage:[UIImage imageNamed:#"memory_green.png"] forState: UIControlStateNormal];
wont change the image each iteration.
But when the for-loop is over the last (random) button in the array will change color to green.
Why arenĀ“t the images updated for every iteration of the for-loop?
Thanks
Never ever use sleep() on the main thread
UI updates don't happen immediately. They happen at the end of the current run loop, that means after your method is done.
You could use a NSTimer. Something like this should work:
- (void)timerFired:(NSTimer *)timer {
_timerCount--;
if (_timerCount <= 0) {
[timer invalidate];
}
// do something
}
- (void)startTimer {
[_timer invalidate];
_timerCount = 10;
_timer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
In my app project I want to be able to tap a button, wait 5 seconds, and then a UILabel and UIImageView appear. Here's what I have so far:
- (IBAction)startTimer
{
responseBox.placeholder = #"Message Here";
timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(timerStop) userInfo:nil repeats:YES];
mainInt +=1;
}
- (void)timerStop
{
if (mainInt == 5)
{
[timer invalidate];
titleLabel.hidden = NO;
messageLabel.hidden = NO;
messageLabel.text = #"Message";
image.hidden = NO;
}
}
Everything's linked correctly, what's going on?
mainInt is only incremented when you press the startTimer button, so that does you no good in timerStop. Also, if you want to increment mainInt correctly, use mainInt++;
Try this:
- (IBAction)startTimer
{
responseBox.placeholder = #"Message Here";
timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(timerStop) userInfo:nil repeats:YES];
mainInt++;
}
- (void)timerStop
{
[timer invalidate];
titleLabel.hidden = NO;
messageLabel.hidden = NO;
messageLabel.text = #"Message";
image.hidden = NO;
}
A simplified set of methods to demonstrate what's happening:
- (void)timerDidFire {
NSLog(#"fire");
}
- (void)resetTimer:(NSTimer *)timer {
if (timer) [timer invalidate]; // timer = nil; here doesn't change anything
NSLog(#"%#", timer);
timer = [NSTimer ...Interval:1 ... repeats:YES];
}
- (IBAction)pressButton {
[self resetTimer:myTimer];
}
Clearing I'm doing something wrong, but what? Why do I get an extra timer with every press?
Each time the resetTimer: method is called you create a new NSTimer instance. Unfortunately after the execution of this method is finished you lost all your references to the new instance because it was assigned to a local variable.
The timer you create inside the method is not assigned to the myTimer variable. Whatever myTimer is, it is not the new created timer.
You could dump all those local variables and simply use something like this:
- (void)resetTimer {
[myTimer invalidate]; // calls to nil are legal, so no need to check before calling invalidate
NSLog(#"%#", myTimer);
myTimer = [NSTimer ...Interval:1 ... repeats:YES];
}
- (IBAction)pressButton {
[self resetTimer];
}
I'm just trying to close an NSPanel after a couple second delay, but I can't get my NSTimer to start. It will fire if I explicitly call the fire method on it, but it will never go by itself. Here's my code:
- (void)startRemoveProgressTimer:(NSNotification *)notification {
NSLog(#"timer should start");
timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(removeProgress:) userInfo:nil repeats:NO];
}
- (void)removeProgress:(NSTimer *)timer {
[progressPanel close];
}
I do have some threading in my code as such. I assume this is what's messing my timer up.
-(void)incomingTextUpdateThread:(NSThread*)parentThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//mark the thread as running
readThreadRunning = TRUE;
const int BUFFER_SIZE = 100;
char byte_buffer[BUFFER_SIZE]; //buffer for holding incoming data
int numBytes = 0; //number of bytes read
NSString *text; //incoming text from the serial port
[NSThread setThreadPriority:1.0];
//this will loop until the serial port closes
while (TRUE) {
//read() blocks until some data is available or the port is closed
numBytes = read(serialFileDescriptor, byte_buffer, BUFFER_SIZE);
if(numBytes > 0) {
//creat a string from the incoming bytes
text = [[[NSString alloc] initWithBytes:byte_buffer length:numBytes encoding:[NSString defaultCStringEncoding]] autorelease];
if(!([text rangeOfString:SEND_NEXT_COORDINATE].location == NSNotFound)) {
//look for <next> to see if the next data should be sent
if(coordinateNum <[coordinatesArray count]) {
[self sendNextCoordinate]; //send coordinates
}
else {
[self writeString:FINISH_COORDINATES_TRANSMIT]; //send <end> to mark transmission as complete
NSNumber *total = [NSNumber numberWithUnsignedInteger:[coordinatesArray count]];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:total forKey:#"progress"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"uploadProgressChange" object:self userInfo:userInfo]; //update progress bar to completed
}
}
[self performSelectorOnMainThread:#selector(appendToIncomingText:) withObject:text waitUntilDone:YES]; //write incoming text to NSTextView
} else {
break; //Stop the thread if there is an error
}
}
// make sure the serial port is closed
if (serialFileDescriptor != -1) {
close(serialFileDescriptor);
serialFileDescriptor = -1;
}
// mark that the thread has quit
readThreadRunning = FALSE;
// give back the pool
[pool release];
}
Which is called from another method by: [self performSelectorInBackground:#selector(incomingTextUpdateThread:) withObject:[NSThread currentThread]];
Thank you rgeorge!!
Adding the timer to the run loop manually made it work!
timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:#selector(removeProgress:) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];