I need to capture the result of NSDate when a button is pressed, so I can use it later on in the app.
What is the best way / code to do this?
Update
Sorry for not including more information. New to this site, and wasn't aware of protocol.
So I understand how to respond to a button press. However, whilst I have written the result of NSDate to the timeStampLabel, what I really want to do is save the result of NSDate at that point to a key? or similar in order that I can use it later. I require the data to be accessible to a different class, however, have no need for the data to persist if the app was closed entirely (i.e. swiped up and off the screen).
The code I currently have is:
- (IBAction)tripButton:(id)sender {
NSDate *date = [NSDate date];
self.timeStampLabel.text = [NSString stringWithFormat:#"%# ", date];
}
You can declare the variable in your App Delegate:
#interface AppDelegate : UIResponder <UIApplicationDelegate, UINavigationControllerDelegate> {
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UINavigationController *navigationController;
#property (nonatomic) NSDate* timeStamp;
#end
Then when you need to access the variable from any class, get an instance of the App Delegate...
First declare the variable right after the implementation in that class's .m:
#implementation ClassViewController
AppDelegate *mainDelegate;
then initialize it in viewDidLoad:
- (void)viewDidLoad {
mainDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
and now you can access it from anywhere. In this case...
- (IBAction)tripButton:(id)sender {
NSDate *date = [NSDate date];
self.timeStampLabel.text = [NSString stringWithFormat:#"%# ", date];
mainDelegate.timeStamp = date;
}
Then in another class, for example:
- (NSDate*)getTimeStamp {
return mainDelegate.timeStamp;
}
So, there are three methods (that I know of) to deal with passing information across different view controllers in iOS.
Lindsey Scott's answer suggests storing the information as properties on the App Delegate and reaching into the app delegate to set/retrieve the data.
Duncan C's answer explains my preferred method, using singletons.
The third method that I know of is passing the information directly between the views that need it. This is simple to manage if you just need to pass the data from one view that is presented immediately before the view you're passing the information to.
Assuming you're using segues, you'd do something like this...
Given ViewController1 and ViewController2, give ViewController2 an NSDate public property to store the date you're passing to it. We'll call it passedDate in this example. We'll also give ViewController1 a property (private, probably) to store the date temporarily, we'll call it dateToPass in this example. Both properties should be strong.
Now, in ViewController1, the IBAction that handles the button press will look like this:
-(IBAction)buttonPressed:(id)sender {
self.dateToPass = [NSDate date];
[self performSegueWithIdentifier:#"segueMySegue" sender: self];
}
So now the button press saves the date to a property and calls the segue. As a note, we don't have to do both actions in this single button press. The main thing that's important is saving the date on the button press to a property so you can access it later. You can perform the segue however/whenever you want.
Now, to actually pass the data, we're going to override the prepareForSegue: method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"segueMySegue"]) {
ViewController2 *dest = [segue destinationViewController];
dest.passedDate = self.dateToPass;
}
}
Now we've set the property passedDate on the next view controller to point to the dateToPass date object we set on the button press. We can access this property in ViewController2 and do with it whatever we need to do.
You have 2 different issues:
How to save a date value for later
How to pass data between different objects in your app.
The first can be solved by making an NSDate or NSTimeInterval property.
The second can be solved various ways. I recommend creating a data container singleton. A singleton is an object that is designed to only be created once in the lifetime of a program and then used throughout. Typically you create a class method called something like sharedDataObject that creates the object the first time you need it, and saves it in a static variable:
header:
#interface DataContainerObject: NSObject
#property (nonatomic, strong) NSDate *dateToSave;
+ (DataContainerObject *) sharedDataObject;
#end
Implementation:
#implementation DataContainerOBject:
+ (DataContainerObject *) sharedDataObject;
{
static DataContainerObject* _sharedDataObject;
if (_sharedDataObject == nil)
_sharedDataObject = [[DataContainerObject alloc] init];
return _sharedDataObject;
}
Related
I am implementing a simple game in which the user tries to guess a randomly selected card. They select their guess from a two-component picker (in FirstViewController), and in the next screen (SecondViewController) they can check if they are correct. I am stuck right now trying to pass the guess the user selected to the screen where they can check.
In SecondViewController.h, I declare properties for both parts of the guess (number and suit) like this:
#property (nonatomic, assign) NSString * guessNumber;
#property (nonatomic, assign) NSString * guessSuit;
Then in didSelectRow in FirstViewController.h, I am trying to pass the info forward like this:
- (void) pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
SecondViewController *secondVC = [[SecondViewController alloc] init];
if (component == 0){
secondVC.guessNumber = _cardNumbers[row];
}else{
secondVC.guessSuit = _cardImages[row];
}
[self.navigationController pushViewController:secondVC animated:YES];
}
In viewDidLoad for SecondViewController, I NSLog the values of guessNumber and guessSuit, but it prints out null for both.
Clearly I am going wrong somewhere and the data isn't actually being passed, if anyone has any guidance about what I need to fix that would be amazing!
EDIT:
I have now changed the values to copy rather than assign, like this:
#property (nonatomic, copy) NSString * guessNumber;
#property (nonatomic, copy) NSString * guessSuit;
I have tried strong as well, and neither work. The weird thing is that if I print out the value right after I assign it, like this:
secondVC.guessNumber = _cardNumbers[row];
NSLog(secondVS.guessNumber);
I get the right value. Now I am even further confused as it seems to be assigning the value but not saving it when I actually go to my second view controller.
The assign tells the NSString * property setter to save the pointer address instead of the OC object itself.
Both _cardNumbers[row] and _cardImages[row] return an autoreleased object which will be released after finishing your didSelectRow method, so you couldn't get the expected string values in your viewDidLoad of SecondViewController.
Use the strong or copy instead, it tells the property setter to increase the value's retain count by 1, so SecondViewController owns (guessNumber owns) the string value, you could get it in its life cycle all the time.
NSString used copy is right . The problem is _cardNumbers and _cardImages ,let' see data source in _cardNumbers.
I am trying to do the following, and not able to find a straightforward answer.. It is related to this :Passing uitextfield from one view to another. But not exactly.
I have a Firstview.m, from which I push to a Secondview.m. The Secondview.m has a UITextView. I allow the user to edit the UITextView on Secondview.m. Now I want to store this text value in a variable in Firstview.m. One way to to do this is as follows
in Firstview.h
#property (nonatomic) Secondview *secondView;
That is keep a secondView variable in Firstview itself. But this doesn't seem efficient. Ideally I should only have 1 NSString text field in FirstView. What is the right way to do this ? Thanks
You can achieve this by using Delegation in Objective-C.
In your SecondView.h add following right after Header Inclusion
#protocol YourDelegateName <NSObject>
-(void)setText:(NSString *)strData;
#end
Also add delegate property to your header for accessing them in calling class, like below (This goes with other properties declaration in SecondView.h file):
#property (nonatomic, weak) id<YourDelegateName> delegate;
Now, Comes the calling the delegate part. Say, you want to save the text value of UITextView of SeconView in strTextViewData of FirstView class, when the following event occurs:
- (IBAction)save:(id)sender
{
[self.delegate setText:self.txtView.text]; // Assuming txtView is name for UITextView object
}
Now, In FirstView.h add YourDelegateName in delegate list like below:
#interface FisrtView : ViewController <YourDelegateName>
#property (nonatomic, reatin) NSString *strTextViewData;
#end
And then in FisrtView.m file when you create instance of SecondView class, set delegate to self like below:
SecondView *obj = [[SecondView alloc] initWithNibName:#"SeconView" bundle:nil];
obj.delegate = self; // THIS IS THE IMPORTANT PART. DON'T MISS THIS.
Now, Implement the delegate method:
-(void)setText:(NSString *)strData
{
self.strTextViewData = strData;
}
Applying this to your code will do what you want. Also, Delegation is one of the most important feature of Objective-C language, which - by doing this - you will get to learn.
Let me know, if you face any issue with this implementation.
I have an iPhone app with three pages, each of which allows the user to enter some text. On the final page I want to concatenate all three strings and print it out. I have a UIViewController (named PageXController) for each page and I am trying to pass variables along to the final page. The method I currently try doesn't quite work. Here is an example:
I begin by declaring a string as an instance variable in PageThreeController.h
#interface PageThreeController : UIViewController{
NSMutableString *string;
}
#property (nonatomic) NSMutableString *string;
Next I add the following to PageOneController.h,
#import "PageThreeController.h"
#interface PageOneController : UIViewController
#property (nonatomic) PageThreeController *pageThree;
In PageOneController I then attempt to set the string instance variable on page three;
- (IBAction)handleButton:(id)sender {
_pageThree = [[PageThreeController alloc] init];
_pageThree.from = [[NSMutableString alloc]init];
[_pageThree.from appendString:#"Hello World"];
NSLog(#"My string is %# on page one.", _pageThree.from);
}
The NSLog prints out My string is 'Hello World' on page one. but when I add the same NSLog on PageThreeController.m before concatenating, 'string' is NULL.
It seems that I am making a separate copy of the pageThreeViewController. What do I need to do to change the actual value of string on page three? I am really new at this
The easiest way to pass data between ViewControllers is using the AppDelegate, even though
there is other methods .
Method 1 - using AppDelegate.
Add the following line in your Appdelegate.
#property (strong,nonatomic) NSMutableString *str;
To access the variable from any view controller,
MyAppdeleagte appDelegate=[[UIApplication sharedApplication]delegate];
NSMutableString *Stringdata=appDelegate.str;
Method 2 -Specifying objects.
in this method , you can proceed as you are now doing and, just need to specify the
view controller instance.
let you have are navigating from one controller to another , say FirstController to Second.
Firstcontroller.h
#interface FirstController : UIViewController{
NSMutableString *string;
}
#property (nonatomic) NSMutableString *string;
SecondController.h
#interface SecondController : UIViewController{
}
#property (strong,nonatomic) FirstController *firstScreen;
Within your implementation of the FirstController, before you navigate to the SecondController ,you have to specify the instance in SecondController.
in FirstController.m
SecondController *nextScreen=[self.storyboard instantiateViewControllerWithIdentifier:#"SecondView"];
nextScreen.firstScreen=self;
Then in your SecondController.m , you can simply get the String as
_firstScreen.string;
Customize your prepareForSegue method in order to pass necessary vars through public properties of these controllers.
For example:
#pragma mark - PreparaForSegue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"goToDetail"]) {
YourDestinationController *destinationController = segue.destinationViewController;
destinationController.myString = self.myStringToPass;
}
}
Where myString is a public property in YourDestinationController and myStringToPass is a property in your source controller (it could be in private scope)
If you need to access it from anywhere, you could store the variable in the AppDelegate. So if you had a variable like this in your AppDelegate:
#property (nonatomic) NSString *currentStringToPass;
Then you could access it from your ViewControllers by using the following code:
- (IBAction)handleButton:(id)sender {
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
_pageThree = [[PageThreeController alloc] init];
_pageThree.from = [[NSMutableString alloc]init];
[app setCurrentStringToPass:#"Hello World"];
NSLog(#"My string is %# on page one.", [app currentStringToPass]);
}
I have a main ViewController that contains a desginated class. Within that ViewController there is a Container that is linked to an embed ViewController. Within that embed ViewController I am creating an NSMutableArray. I am not trying to access that array inside the main ViewController. I know that if I use:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
NSLog(#"%#",myScript.selectedCells);
The NSLog will output null because I am creating a new ViewController and that gets rid of the already set array. So my question is how can I access that array without overwriting it?
UPDATE:
Heres where the NSMutableArray is being created:
create_challenge_peopleSelect.h:
#property (strong, nonatomic) NSMutableArray *selectedCells;
create_challenge_peopleSelect.m:
if([selectedCells containsObject:label.text])
{
cell.accessoryType = UITableViewCellAccessoryNone;
[selectedCells removeObjectIdenticalTo:label.text];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[selectedCells addObject:label.text];
}
This class is the container class off the main ViewController
No I want to access the selectedCells within my main ViewController, I have been doing things such as:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
I would prefer to stay away from the App Delegate If possible.
You seem to be unclear on the difference between classes and instances. OK, so, say we have two NSArrays:
NSArray *a = [[NSArray alloc] initWithObjects:#"hello", #"I", #"am", #"an", #"array", nil];
NSArray *b = [[NSArray alloc] initWithObjects:#"so", #"am", #"I", nil];
If I do a.count, I'll get 5 as the answer because the array contains five objects. Meanwhile, if I do b.count, I'll get 3, because that array contains three objects. It isn't that creating b "gets rid of the already set count". They are separate objects completely unrelated to each other.
Your view controller class is the same way. When you create a different instance, it doesn't overwrite the old one -- it's just not the same object. In order to use the original view controller object, you need to get a reference to it.
So how do you get a reference to it? Well, the general answer is you design your app so that the two objects know about each other. There are lots of specific ways to accomplish this. A lot of people will say "Just stick a reference in the app delegate." That is one thing you can do, but it's not always the best choice. It can get out of control if you just stick everything in your app delegate. Sometimes it's the right answer, often other things are the right answer. Another approach is to have an object that knows about both of those objects introduce them to each other. But sometimes there is no such object. So it's situational.
Basically, instead of creating a new view controller, you need to maintain a pointer to the original.
I suggest storing an instance of your UIViewController in the AppDelegate in order to retain the particular instance of the view controller you've created by making it a global variable.
ex. In the App Delegate.h
#import "ViewController.h"
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic) ViewController *viewController;
Then from whatever view controllers' .m's from which you need to read/write to the variable, create a pointer to the application's app delegate, ex:
#import "AppDelegate.h"
#interface WhateverViewController ()
AppDelegate *mainDelegate;
- (void)viewDidLoad {
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
}
So wherever you first create that view controller in your code (before ever using it), initialize it using this global variable. ex. If you're using xibs:
mainDelegate.viewController = [[UIViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
ex. If you're using storyboards:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
mainDelegate.viewController = [storyboard instantiateViewControllerWithIdentifier:#"viewControllerID"];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
(This is assuming it's in a place other than the app delegate in which case the pointer to the App Delegate isn't needed.)
Then when accessing the array from another UIViewController use
mainDelegate.viewController.array
To access the NSMutableArray from one class to another class use following code.
In the first view controller in which u have declared the object of NSMutableArray, declare the property and synthesize for the same as below,
//In FirstViewcontroller.h class,
#property (nonatomic, strong) NSMutableArray *arrData;
//In FirstViewcontroller.m class
#synthesize arrData;
Also FirstViewcontroller object should be global so you can create the object of FirstViewcontroller in app delegate file.
//appdelegate.h
#property (nonatomic, strong) FirstViewcontroller *objFirst;
//appdelegate.m
#synthesize objFirst;
FirstViewcontroller *objFirst=[[FirstViewcontroller alloc]init];
Now in SecondViewcontroller in which you have to access array,
create the share object of Appdelegate file
//SecondViewcontroller.m
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then use will get the required array as below,
app.objFirst.arrData
This is your required array I hope it will help you.
The basic idea here is that in your original class, the array is referred to by a pointer. Your original class would allocate it and presumably load it. Other parts of your program can be handed the contents of the property, which is a pointer, assign that to their own pointer holder, and use it as if you had declared it there. Please use the above code;
MyClass *aClass = [[MyClass alloc] initWithMyInitStuff];
NSMutableArray *ThatArray = aClass.MyArray;
NSLog("Count of ThatArray: %d", [That.Array count]);
What you've done in the code provided is set a public property for a mutable array...
#property (strong, nonatomic) NSMutableArray *selectedCells;
The NSMutableArray is not "created" by setting that property. At some point in your code you also have to create the NSMutableArray by initialising...
NSMutableArray *selectedCells = [[NSMutableArray alloc] init];
or by using a convenience method such as...
NSMutableArray *selectedCells = [NSMutableArray arrayWithCapacity:(NSUInteger)<initialising capacity>];
or
NSMutableArray *selectedCells = [NSMutableArray arrayWithArray:(NSArray *)<initialising array>];
Initialising an NSMutableArray is often done only once. If it is repeated, the contents are overwritten against the property used to point to the array. As such, a useful location for this is often within the viewDidLoad view controller lifecycle method.
I am making an app using a utility application template. I am trying to access the value of a UITextField from the FlipSideVewController class.
In the MainViewController.h file I have -
#interface MainViewController : UIViewController <UISplitViewControllerDelegate>{
UITextField *textField;
NSString *myText;
}
#property (retain, nonatomic) IBOutlet UITextField *textField;
#property (nonatomic, retain) NSString *myText;
-(IBAction)pressButton:(id)sender;
In the MainViewController.m file -
myText = [[NSString alloc] initWithFormat: textField.text];
NSLog(#"%#",myText);
I am creating the FlipSideViewController in the MainViewController class using the following code -
FlipsideViewController *controller = [[[FlipsideViewController alloc] initWithNibName:#"FlipsideViewController" bundle:nil] autorelease];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
This prints the value of the textfield in the console without any problems. The problem happens when I try to access the value of the textfield in the FlipSideVewController class (after the user presses the go button).
In the FlipViewController class I have -
MainViewController *obj = [[MainViewController alloc] init ];
NSString *abc = obj.textField.text;
NSLog(#"%#",abc);
The FlipSideVewController nib file is loaded fine without any problems. However the console output is (null) when in FlipSideVewController.
I will appreciate any help.
Thanks
If you use the Utility Xcode template, you should think of MainViewController and FlipSideVewController as given: the framework will instantiate them for you and make it available to the rest of the program as you define (either in your code or in IB). What I mean by this is that your line:
MainViewController *obj = [[MainViewController alloc] init ];
does not really do what you want.
In your case, what you seems to want is access a text field controlled by the existing MainViewController instance (the one that gives you the NSLog output correctly) from your other existing FlipSideVewController instance. This is a matter of "connectin" somehow those two controllers.
There are several ways to accomplish this. One is defining a "model" for your app and have it shared between the controllers. See also the Model-View-Controller pattern. A model is just a data structure that contains your "data"; you make that data structure available to both of your controllers. The easiest way to create such data structure and have it shared is through a singleton (I am not suggesting to use it as the best way, just noting that it is the easiest way, IMO).
Another, less clean way is passing a reference to MainViewController to FlipSideVewController and then accessing the text field through it. By example, you could define an ivar in your FlipSideVewController, then, where the two controllers are created, you do the assignment to the ivar.
You should go to your MainViewController and declare your textField as a property first and synthesize it, so you can access it using obj.textField. And if you have just created obj using alloc and init, you wont have any text in the textField instance Variable.
MainViewController.h
#property (retain) UITextField *textField;
MainViewController.m
#synthesize textField;
and you could use
myText=textField.text;
Now this should do it and you can access this textField by obj.textField in your other class. But you still wont get its value if you are initializing it in your other class because you will be creating a brand new obj whose textField.text will be blank( unless you have overrided its designated initializer to set the textField.text value).
Declare NSString *abc as instance variable
NSString *abc;
and then as property
#property (copy) NSString *abc;
#synthesize abc;
After you create your FlipSideViewController,
controller.abc=myText;
Remove the code where you create obj.
This will do it.