Reputation: 534
Right, I'm trying to make an app that has a calculation that involves a stopwatch. When a button on the calculation view is clicked a stopwatch slides in from the bottom. This all works fine, the problem I can't get my head around is how to send the recorded time back to the previous controller to update a textfield.
I've simplified the code and stripped out most irrelevant stuff.
Many thanks.
CalculationViewController.h
#import <UIKit/UIKit.h>
@interface CalculationViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITextField *inputTxt;
}
@property (nonatomic, retain) UITextField *inputTxt;
- (IBAction)showTimer:(id)sender;
@end
CalculationViewController.m
#import "CalculationViewController.h"
#import "TimerViewController.h"
@implementation CalculationViewController
- (IBAction)showTimer:(id)sender {
TimerViewController *timerView = [[TimerViewController alloc] init];
[self.navigationController presentModalViewController:timerView animated:YES];
}
TimerViewController.h
#import <UIKit/UIKit.h>
@interface TimerViewController : UIViewController {
IBOutlet UILabel *time;
NSTimer *myTicker;
}
- (IBAction)start;
- (IBAction)stop;
- (IBAction)reset;
- (void)showActivity;
@end
TimerViewController.m
#import "TimerViewController.h"
#import "CalculationViewController.h"
@implementation TimerViewController
- (IBAction)start {
myTicker = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(showActivity) userInfo:nil repeats:YES];
}
- (IBAction)stop {
[myTicker invalidate];
#Update inputTxt on calculation view here
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)reset {
time.text = @"0";
}
- (void)showActivity {
int currentTime = [time.text intValue];
int newTime = currentTime + 1;
time.text = [NSString stringWithFormat:@"%d", newTime];
}
@end
Upvotes: 0
Views: 468
Reputation:
A couple of ways to do this are:
In both cases, the TimerViewController would notify CalculationViewController it is done and at the same time pass the data with it. So in the stop method, it would do postNotificationName:object:userInfo or call a delegate method. Then the dismiss would instead be done in CalculationViewController when it receives the notification.
EDIT:
There isn't necessarily one right way every time. Depending on the size and complexity of the app and exact requirements of the situation, some ways are better than others. The app delegate is ok for sharing data across view controllers but it's probably better to use notifications or delegates to signal events across controllers like you want to do here.
A protocol is a stricter and more self-documenting and self-contained approach than notifications but for this simple case, either one is fine.
Here's how you can implement it using a protocol/delegate approach:
TimerViewController.h:
@protocol TimerViewDelegate
-(void)timerStopped:(NSString *)timerData;
@end
@interface TimerViewController : UIViewController {
//other ivars...
id<TimerViewDelegate> delegate;
}
//other properties...
@property (nonatomic, assign) id <TimerViewDelegate> delegate;
//method declarations...
@end
TimerViewController.m:
@implementation TimerViewController
@synthesize delegate;
- (IBAction)stop {
[myTicker invalidate];
NSString *timerData = @"timer data here";
[self.delegate timerStopped:timerData];
}
@end
CalculationViewController.h:
#import "TimerViewController.h"
@interface CalculationViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource, TimerViewDelegate > {
...
}
@end
CalculationViewController.m:
- (IBAction)showTimer:(id)sender {
TimerViewController *timerView = [[TimerViewController alloc] init];
timerView.delegate = self;
[self.navigationController presentModalViewController:timerView animated:YES];
[timerView release];
}
- (void)timerStopped:(NSString *)timerData
{
inputTxt.text = timerData;
[self dismissModalViewControllerAnimated:YES];
}
And here's the notification version:
CalculationViewController.m:
- (IBAction)showTimer:(id)sender {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(timerStopped:) name:@"timerStopped" object:nil];
TimerViewController *timerView = [[TimerViewController alloc] init];
[self.navigationController presentModalViewController:timerView animated:YES];
[timerView release];
}
- (void)timerStopped:(NSNotification*)notification
{
NSString *timerData = [[notification userInfo] objectForKey:@"timerData"];
inputTxt.text = timerData;
[self dismissModalViewControllerAnimated:YES];
}
TimerViewController.m:
- (IBAction)stop {
[myTicker invalidate];
NSDictionary *dict = [NSDictionary dictionaryWithObject:@"timer data here" forKey:@"timerData"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"timerStopped" object:self userInfo:dict];
}
In this very simple case, a notification looks ok to use. In either case, neither controller has to know the inner details of each other nor do they depend on a third party like the app delegate. They only have to agree on the notification name and userinfo keys. The protocol approach is even more strict but self-documenting.
Upvotes: 2