NSTimer Troubles - cocoa

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]

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

Xcode How to stop a timer Label with an if statement when a UITextField has the same text as a UILabel

I have been trying to do this on an app for a long time but I cannot get it to work, please could someone help me and post some code on how I would do it. What I need to happen is when the text in the UITextField equal the UILabel. Thanks
h. viewcontroller
#interface AlphabetSceneViewController : UIViewController {
UILabel *stopWatchLabel;
NSTimer *stopWatchTimer;
NSDate *startDate;
IBOutlet UILabel *wordToType;
IBOutlet UITextField *wordTyped;
and the m. view controller
- (void)updateTimer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss.SSS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString=[dateFormatter stringFromDate:timerDate];
stopWatchLabel.text = timeString;
}
- (IBAction)onStartPressed:(id)sender {
startDate = [NSDate date];
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];
if ([wordToType.text isEqualToString:#"stop"]) {
}
}
Help much appreciated!
I figured out how to do it, I'll put up the code for anyone that wanted to know. It's only code for the .m file and the alert view is just a bit of extra code on the end to make it show an alert when with the time that the user had when the timer stops.
- (IBAction)stopTimer:(id)sender {
if([wordTyped.text isEqualToString:wordToType.text]){
[stopWatchTimer invalidate];
stopWatchTimer = nil;
[self updateTimer];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Nice Work" message:[NSString stringWithFormat:#"Good Job! You're time to type the alphabet was %#. Let's see if you can do better...", stopWatchLabel.text]
delegate:self cancelButtonTitle:#"Main Menu" otherButtonTitles: nil];
[alert show];
}
}

Using A NSTimer for MM:SS:HS?

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];
}

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.

Resources