Using A NSTimer for MM:SS:HS? - xcode

I have created a NSTimer In Xcode 4.2 and it works but i get this one problem.
here is my project in the simulator
when i press start it starts and when i press stop it stops and when its stopped it will reset
but when it starts and i press reset when it is going nothing happens it don't reset when started basically you have to stop then reset is the ways and this or do i need to add code any where heres a copy of my code.
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController {
IBOutlet UILabel *time;
NSTimer *myticker;
//declare baseDate
NSDate* baseDate;
}
-(IBAction)stop;
-(IBAction)reset;
#end
heres my implementation
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize baseDate;
-(IBAction)start {
[myticker invalidate];
self.baseDate = [NSDate date];
myticker = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
}
-(IBAction)stop;{
[myticker invalidate];
myticker = nil;
}
-(IBAction)reset {
self.baseDate = [NSDate date];
time.text = #"00:00:0";
}
-(void)showActivity {
NSTimeInterval interval = [baseDate timeIntervalSinceNow];
double intpart;
double fractional = modf(interval, &intpart);
NSUInteger hundredth = ABS((int)(fractional*10));
NSUInteger seconds = ABS((int)interval);
NSUInteger minutes = seconds/60;
time.text = [NSString stringWithFormat:#"%02d:%02d:%01d", minutes%60, seconds%60, hundredth];
}
I Really Appreciate It. Thanks.

First, the above should crash with EXC_BAD_ACCESS when it reaches showActivity since baseDate is not being retained in the start method. [NSDate date] returns an autoreleased object so baseDate will have an invalid reference after the start method.
I suggest changing baseDate to a retain property and then setting it in start using self.:
//.h
#property (nonatomic, retain) NSDate *baseDate;
//.m
#synthesize baseDate;
-(IBAction)start {
[myticker invalidate];
self.baseDate = [NSDate date];
myticker = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
}
To fix the reset problem, note that the showActivity method takes the current value of baseDate to calculate the elapsed time and then sets the time label to display it formatted.
In the start method, you set the baseDate to the current time (you don't set time.text) and then start the timer. The showActivity method will then keep firing and set time.text.
In the reset method, you want the timer to start showing the elapsed time since the moment reset is pressed. The timer is already running so you don't need to re-start it. Setting the time label text doesn't work because when the already-running timer fires again, it will calculate the elapsed time from baseDate which is still the original start time and then set time.text based on that. So instead of setting time.text, set the baseDate:
-(IBAction)reset {
self.baseDate = [NSDate date];
}

Related

OSX - cannot set an NSTextField with a string from an NSTimer

I cannot seem to set a timer output to an NSTextField. I've looked at this Q/A, among many, but must be doing something different but wrong.
I initialize the timer and the textfield in WindowDidLoad.
My timer method is as follows:
time += 1;
if((time % 60) <= 9) {
NSString *tempString = [NSString stringWithFormat:#"%d:0%d",(time/60),(time%60)];
[timerTextField setStringValue:tempString];
} else {
NSString *tempString = [NSString stringWithFormat:#"%d:%d",(time/60),(time%60)];
timerTextField.stringValue = tempString;
}
NSLog(#"timerTextField: %#", timerTextField.stringValue);
As you can see, I log the textfield output.
I have an IBOutlet connection from the the File's owner to the timerTextField.
I can see the output correctly in the log, but the textfield string is not populated.
Can anyone see anything I'm doing wrong? Thanks
The code you posted looks OK to me. (You could replace it by binding your timerTextField to the time value via Cocoa Bindings though).
How did you create your timer and to which runloop(mode) did you add it?
Another problem could be that the outlet to your timerTextField is not connected. Can you set a breakpoint in your timer selector and check if timerTextField is not nil.
Update:
I just tried your code and for me it works.
Given that you have an IBOutlet named timerTextField connected to a NSTextField instance in Interface Builder, this code updates the textfield each second:
#interface SSWAppDelegate ()
{
NSUInteger time;
}
#property (weak) IBOutlet NSTextField *timerTextField;
#end
#implementation SSWAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSTimer* timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
- (void)updateTime:(id)sender
{
time += 1;
if((time % 60) <= 9) {
NSString *tempString = [NSString stringWithFormat:#"%lu:0%lu",(time/60),(time%60)];
[self.timerTextField setStringValue:tempString];
} else {
NSString *tempString = [NSString stringWithFormat:#"%lu:%lu",(time/60),(time%60)];
self.timerTextField.stringValue = tempString;
}
NSLog(#"timerTextField: %#", self.timerTextField.stringValue);
}
#end

How not to Release a View in Xcode

Hello I have a stopwatch view and when the user clicks on the back button to go to another view the stopwatch will stop. I had people tell me that is because it is being released.
I want the stop watch to keep running.
So how do I un-release or not allow it to be released?
Here i my code.
.h
UILabel *stopWatchLabel;
NSTimer *stopWatchTimer; // Store the timer that fires after a certain time
NSDate *startDate; // Stores the date of the click on the start button
#property (nonatomic, retain) IBOutlet UILabel *stopWatchLabel;
- (IBAction)onStartPressed:(id)sender;
- (IBAction)onStopPressed:(id)sender;
.m
- (void)viewDidUnload
{
[self setStopWatchLabel:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)updateTimer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.SSS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString=[dateFormatter stringFromDate:timerDate];
stopWatchLabel.text = timeString;
}
- (IBAction)onStartPressed:(id)sender {
startDate = [NSDate date];
// Create the stop watch timer that fires every 10 ms
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
- (IBAction)onStopPressed:(id)sender {
[stopWatchTimer invalidate];
stopWatchTimer = nil;
[self updateTimer];
}
I would try a different approach, than to change the view controller's behavior:
You can make the timer independent from the view controller by moving the timer functionality into a separate object. That way the timer object has a different life cycle than the view controller object. For example, you can create (and release) the timer object in the application delegate, and create a reference to it and access it in your view controller.

Using a NSTimer for HH:MM:SS?

How can I change this code so it has HH:MM:SS (hours, minutes, seconds)?
Do I need to add code in .h or .m so I known which one?
At the moment it goes up like 1, 2, 3, 4 etc.
Just to let you known I'm bait of a amateur would you copy and past so I known what you mean.
.h
#interface FirstViewController : UIViewController {
IBOutlet UILabel *time;
NSTimer *myticker;
//declare baseDate
NSDate* baseDate;
}
-(IBAction)stop;
-(IBAction)reset;
#end
.m
#import "FirstViewController.h"
#implementation FirstViewController
-(IBAction)start {
[myticker invalidate];
baseDate = [NSDate date];
myticker = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
}
-(IBAction)stop;{
[myticker invalidate];
myticker = nil;
}
-(IBAction)reset;{
time.text = #"00:00:00";
}
-(void)showActivity {
NSTimeInterval interval = [baseDate timeIntervalSinceNow];
NSUInteger seconds = ABS((int)interval);
NSUInteger minutes = seconds/60;
NSUInteger hours = minutes/60;
time.text = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes%60, seconds%60];
}
First, declare baseDate variable in your FirstViewController.h, like this:
#interface FirstViewController : UIViewController {
IBOutlet UILabel *time;
NSTimer *myticker;
//declare baseDate
NSDate* baseDate;
}
Then, in the FirstViewController.m start method add baseDate = [NSDate date] like this:
-(IBAction)start {
[myticker invalidate];
baseDate = [NSDate date];
myticker = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
}
After that, change your showActivity method to look like this:
-(void)showActivity {
NSTimeInterval interval = [baseDate timeIntervalSinceNow];
double intpart;
double fractional = modf(interval, &intpart);
NSUInteger hundredth = ABS((int)(fractional*100));
NSUInteger seconds = ABS((int)interval);
NSUInteger minutes = seconds/60;
NSUInteger hours = minutes/60;
time.text = [NSString stringWithFormat:#"%02d:%02d:%02d:%02d", hours, minutes%60, seconds%60, hundredth];
}
Also note, that you will have to change the interval value for your timer, otherwise your label will only be updated once a second.

Xcode4 How do I play one timer and jump to another then back as a loop?

It's a bit more complex however...
I need two NSTimers:
myTimer1 will have a speed of 1 sec. intervals and play for 6
intervals stop and jump to myTimer2...
myTimer2 will have a speed of 2 sec. intervals and play for 6
intervals and then jump back to myTimer1
and continue this until a stop button is touched.
I have managed to only get the one timer to play and can figure out how to do the rest.
Here is the code synopsis I used:
.h
#interface sosTest8ViewController : UIViewController {
IBOutlet UILabel *myCounterLabel;
IBOutlet UIView *greenView;
IBOutlet UIView *blueView;
NSTimer *myTimer1;
NSTimer *myTimer2;
}
#property (nonatomic, retain)NSTimer *myTimer1;
#property (nonatomic, retain)NSTimer *myTimer2;
#property (nonatomic, retain) UILabel *myCounterLabel;
#property (nonatomic, retain) UIView *greenView;
#property (nonatomic, retain) UIView *blueView;
#end
.m
#synthesize myCounterLabel;
#synthesize greenView;
#synthesize blueView;
#synthesize myTimer1;
#synthesize myTimer2;
- (void)viewDidLoad
{
[super viewDidLoad];
self.myCounterLabel.text = #"0";
myTimer1 = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
}
-(void)updateCounter:(NSTimer*)theTimer{
static int count = 1;
count++;
NSString *s = [[NSString alloc]initWithFormat:#"%d", count];
self.myCounterLabel.text = s;
[s release];
self.blueView.hidden = YES;
self.greenView.hidden = YES;
switch (count & 0x01) {//was & 0x03
case 0: self.blueView.hidden = NO; break;
case 1: self.greenView.hidden = NO; break;
}
//I added this if bit as my own way, but doesn't jump to the net timer properly
if (count >= 3) {
[myTimer1 invalidate];
self.myCounterLabel.text = #"0";
myTimer2 = [NSTimer scheduledTimerWithTimeInterval:2.0f
target:self
selector:#selector(updateCounter2:)
userInfo:nil repeats:YES];}
So this is the formula I've been playing with and before I added the "if" statement it worked and the two colored UIViews alternated fine. When I change the speed to two in myTimer1 to 2 that works in changing the speed interval. I just need help using a working syntax to add the second timer jump and then looping it.
It may not even be possible to do this, but I hope it is and someone can help me.
Many thanks.
--Rob
I would create Two NSStrings:
NSString *countone
NSString *countwo
Then in your selector for your first timer:
if ([countone isEqualToString:#""]) {
//do your stuff
countone = #"1";
}
else if ([countone isEqualToString:#"1"]) {
//do stuff
countone = #"2";
}
else if ([countone isEqualToString:#"2"]) {
//do stuff
countone = #"3";
}
and so on until you get to:
else if ([countone isEqualToString:#"6"]) {
//stop your first timer
//start your second timer
countone = #"";
}
And you can do exactly the same for your second timer, just the orher way round so that at the end you start your first timer, and like that it loops over and over!
And to stop the timer do:
[Timer invalidate];
Timer=nil;

NSTimer Troubles

I am trying to run the code below but it keeps locking up my simulator after the "Tick" is written to the console. It never outputs "Tock" so my guess is that it has to do with the line "NSTimeInterval elapsedTime = [startTime timeIntervalSinceNow];" The IBactions are activated by buttons. timer and startTime are defined in the .h as NSTimer and NSDate respectively.
Can anyone tell me what I am doing wrong?
code:
- (IBAction)startStopwatch:(id)sender
{
startTime = [NSDate date];
NSLog(#"%#", startTime);
timer = [NSTimer scheduledTimerWithTimeInterval:1 //0.02
target:self
selector:#selector(tick:)
userInfo:nil
repeats:YES];
}
- (IBAction)stopStopwatch:(id)sender
{
[timer invalidate];
timer = nil;
}
- (void)tick:(NSTimer *)theTimer
{
NSLog(#"Tick!");
NSTimeInterval elapsedTime = [startTime timeIntervalSinceNow];
NSLog(#"Tock!");
NSLog(#"Delta: %d", elapsedTime);
}
I have the following in the .h:
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {
NSTimer *timer;
NSDate *startTime;
}
- (IBAction)startStopwatch:(id)sender;
- (IBAction)stopStopwatch:(id)sender;
- (void)tick:(NSTimer *)theTimer;
#property(nonatomic, retain) NSTimer *timer;
#property(nonatomic, retain) NSDate *startTime;
#end
Where you have:
startTime = [NSDate date];
You need:
startTime = [[NSDate date] retain];
Anything that is created with out an alloc, new, init will be auto-released (rule of thumb). So what is happening is you are creating the NSDate, assigning it to startTime, it's getting auto-released (destroyed), then you are trying to call timeIntervalSinceNow on an object that was fully released so it blows up.
Adding the retain increased the retain count so it still sticks around after the auto-release. Don't forget to manually release it when you're done with it though!
To take advantage of the #property you need to do:
self.startTime = [NSDate date]

Resources