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.
Related
I am creating a simple IOS Application will a countdown timer. When I run the app in the simulator the countdown timer doesn't countdown. It simply stays at the current time.
Also any suggestions on making the numbers line up would be nice.
My .h file
// ViewController.h
// ******
//
// Created by ***** on 7/29/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
NSDate *destinationDate;
IBOutlet UILabel *datelabel;
NSTimer *timer;
}
#end
My .m file
//
// ViewController.m
// ******
//
// Created by ******* on 7/29/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "ViewController.h"
#implementation ViewController
-(void) updateLabel {
NSCalendar *calender = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
int units = NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *components = [calender components:units fromDate:[NSDate date] toDate:destinationDate options:0];
[datelabel setText:[NSString stringWithFormat:#"%d%c %d%c %d%c %d%c", [components day], ' ', [components hour], ' ', [components minute], ' ', [components second], ' ']];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
destinationDate = [[NSDate dateWithTimeIntervalSince1970:1343609103] retain];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateLabel) userInfo:nil repeats:NO];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
set repeats YES to your timer.
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
edit: also consider using NSDateFormatter to display your string.
If you want to improve your layout, I would suggest using NSDateFormatter in conjunction with multiple UILabels. The date formatter can be used to create a string of the format you require and each label can hold a particular part of the date/time string. Take a look at Apple's Data Formatting Guide the most understated part is the link to the Unicode Technical Standard #35 which defines all the formats you can use.
Once you've split your date, you just have to layout your views as you like.
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];
}
}
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];
}
I have a stopwatch that runs fine on my app however when I click on the back button to go to another view it stops. I want the stopwatch to keep running even when the user is viewing another page.
How do I accomplish this?
Ok added the code
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];
}
Well it stops because you're releasing the view that it lives in when you pop it off the stack.
have you tried making a stopwatch class that handles the timer independent of the view?
The stopwatch stops because you're releasing the view that controls it.
You need to make sure that the NSTimer is not linked to any view.
Perhaps you can use subviews or Modal View Controllers (I think that may work) in order to not have the stopwatch released when the view is.
You could also try sending the stopwatch information to the next view through variables, and then having the other view take over the stopwatch from there.
There are most definitely other methods as well.
Either way, you need to make sure that the NSTimer is not released.
Exactly, if you look at your "ViewDidUnload" you're setting it to nil when the view gets released which it does when you go to another view.
Something you can try is to have a class that's running the timer and sending it over to the view that way it's still running even though the view that displays it is not in memory.
MVC style!
Yeah, what everyone else says.
On the ViewDidUnload it is releasing it. Meaning it is basically ending the operation as soon as that ViewController is exited.
I Dont know if the problem is already solved or not but still.
Create the Object of NSTimer in App Delegate Class and then instantiate the timer of in .m file of app delegate in this way your NSTimer will stay in memory. and when you want to stop or pause the timer you can just invalidate that object from any view Controller.
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]