What I am trying to do is display a calculated variable from my FirstViewController.m file in a text field in my SecondViewController.m file. The simplest way I found to do this (although probably not the best) was by using NSUserDefaults. The problem I am encountering is that when using the app if I go back to the first tab and change the value of the variable, the text field in the second tab does not refresh to reflect this when I go back onto it. I want it to change automatically without the user having to press a button. Is there any way of doing this? Also a better way to access the variable from the second class would be very useful.
//FirstViewController.m
//Calculation of epleyInt using other text fields and pressing a button.
[epleyField setText:[NSString stringWithFormat:#"%i", epleyInt]];
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
[settings setInteger:epleyInt forKey:#"epley"];
[settings synchronize];
and
//SecondViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
int epleyInt = [settings integerForKey:#"epley"];
[epley10Field setText:[NSString stringWithFormat:#"%i", epleyInt]];
}
Managed to do it using singleton variables and moving the code to update the textfield to the viewDidAppear method.
I found this very helpful: Simple Passing of variables between classes in Xcode
Related
I have a couple of objects stored in the user's NSUserDefaults which I have to use more or less in every single ViewController of my app.
Currently, I basically have the same 3 variables declared, and in the viewDidLoad I initialise them like:
if(....){
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx"] integerValue];
chosenName = [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
}else{
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx_2nd option"] integerValue];
...
}
I'm looking to clean up my code and optimize my code, and I was wondering what the right way to handle a case such like this was, to avoid having these 10-12 exact same lines of code at the start of every single ViewController.
Write an utility class. And create some class methods.
One method can be like,
+ (NSString *)choosenName {
return [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
}
And call the method like,
chosenName = [Your_Utility_Class choosenName];
Yes you can achieve it globally by following simple method.Create NSObject class,please refer my example below.
.h File
//Setting up Session
+(void)SetEmail:(NSString*)value;
+(void)SetFirstName:(NSString*)value;
//Retrieve
+(NSString*)GetEmail;
+(NSString*)GetFirstName;
.m file
+(void)SetEmail:(NSString *)value{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:#"EMAILID"];
}
+(void)SetFirstName:(NSString *)value{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:#"FIRSTNAME"];
}
+(NSString*)GetEmail{
return [[NSUserDefaults standardUserDefaults] stringForKey:#"EMAILID"];
}
+(NSString*)GetFirstName{
return [[NSUserDefaults standardUserDefaults] stringForKey:#"FIRSTNAME"];
}
Now Move to Viewcontroller and access without alloc init as it is in Class method.I am setting up from result.
Setting up in Viewcontroller
[NSDefaultSession SetEmail:#"YourString"];
[NSDefaultSession SetFirstName:#"YourString"];
Now Getting Session from any ViewController
[NSDefaultSession GetEmail]
[NSDefaultSession GetFirstName]
Firstly you should put these values into an object, and secondly use dependency injection.
So first make a class Chosen (for want of a better name) and give it the properties id and name. Now the only thing that needs to worry about where the data is saved and loaded from is the 'Chosen' object, everything else will go through that.
Ok now the dependency injection. You want your VC dependencies to be obvious and clear, don't rely in singletons like NSUserDefaults hidden away inside them. So make .chosen a public property on each of the VC's that needs access to the object.
Init this object in application:application didFinishLaunchingWithOptions: and now inject that into your initial viewController. (ie, set the public property)
Now just pass along the object again by injection to each of the other viewController that needs access to it.
in my option:
create commonClass (sub class of NSObject class)
crate spare methods and use them where u need.
ex:
in ur vc1:
set the values for ur objects
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx"] integerValue];
chosenName = [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
in ur common class:
+(NSString *) chosenID{
NSUserDefaults *serverDefults=[NSUserDefaults standardUserDefaults];
NSString * chosenID =[serverDefults objectForKey:#"chosen_xxxx"];
if (chosenID.length==0) {
// do some actions
}
return chosenID;
}
in another VC2:
NSString *id =[commonClass chosenID];
This is the right way to address this issue. Common functionality is what sub-classing is all about - your view controllers are a specific type of view controller that needs access to these variables.
Create class BaseViewController, which is a sub-class of a UIViewController.
Give BaseViewController two public properties called chosenId and chosenName.
Add the init code you have to the viewDidLoad of BaseViewController.
Remove the init code from each of your existing view controllers.
Make any view controller that requires these variables of type BaseViewController intead of UIViewController.
Those variables are now magically (and consistently) available in all of your view controllers without any code duplication.
I have 2 views, a login view and a main view.
I use SWRevealViewController, and I want automatically display my menu at the startup of the app. I know how display my menu but I don't know how display it just once at startup.
I want to pass a simple String between my Login view and my Main view but without segue, and made a simple test :
if (myPreviousView == "LoginView")
{
// display my menu
}
Another method would be to use NSUserDefault to store your string, which than can be accessed from anywhere within the application.
So, you put your string into NSUserDefaults in your first view:
// Initialize the NSUserDefaults object and an array for data storage
NSUserDefaults *defsData = [NSUserDefaults standardUserDefaults];
NSMutableArray *myArray = [[NSMutableArray alloc] init];
// Add your string to the custom array
NSString *myString = #"My string.";
[myArray addObject:myString];
// Put the array back into UserDefaults for later use
[defsData setObject:myArray forKey:#"Key"]; // Key can be anything
[defsData synchronize];
Now, the array (and the string in it) is available anywhere. So, when you navigate to your second view controller, just initialize an NSUserDefaults and access the string:
NSUserDefaults* defsData = [NSUserDefaults standardUserDefaults];
NSArray *myArray = [defsData objectForKey:#"Key"];
NSLog("This is my stored string: %#", [myArray objectAtIndex:0]);
You can modify the init method of your second view controller to take a custom attribute when you subclass it. So, lets say you created a standard UIViewController (.h and .m files). You can modify the init method of this new class to your liking in the .h file:
- (instancetype)initWithString:(NSString *)string;
And then replace the standard init with the new one in the .m:
- (instancetype)initWithString:(NSString *)string {
}
So, when you call your view controller into existence, you just use this new init method and pass the string you wanted like this:
UIViewController *viewController = [[UIViewController alloc] initWithString:myString];
[self presentViewController:viewController animated:NO completion:nil];
This is a programmatical approach of course, but it should be applied to interface builder easily (unfortunately, as I never use interface builder, I don't know how exactly, but as I said, it should be fairly straightforward to anyone who uses it).
I am pretty new to iOS so here's my question. I have a Table View (GuestTableViewController) listing some guests in a party. When I click in a person, I show a new view (GuestInfoViewController) with some info about this attendee. In this view I have switch button, so if I have 3 persons, there will be 3 switches indicating each one of them is coming or not.
Using NSUserDefaults in a IBAction in my GuestInfoViewController I have achieved to save its state (ON/OFF) between views.
The problem is that when I click one switch, all switches change state. How can reference each one of the switches.
Note: I can post images on my storyboard or even some code if needed.
Thank you so very much!
#implementation GuestInfoViewController
#synthesize nom,cognoms,foto;
#synthesize setNom,setCognoms,setFoto;
#synthesize mySwitch;
- (void)viewDidLoad
{
[super viewDidLoad];
nom.text = setNom;
cognoms.text = setCognoms;
[foto setImage:[UIImage imageNamed:setFoto]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"SwitchState"])
self.mySwitch.on = [defaults boolForKey:#"SwitchState"];
}
- (IBAction)switch:(id)sender {
if(mySwitch.on){
NSLog(#"Switch is ON");
}
if(!mySwitch.on){
NSLog(#"Switch is OFF");
}
}
- (IBAction)saveSwitchState:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([self.mySwitch isOn])
[defaults setBool:YES forKey:#"SwitchState"];
else
[defaults setBool:NO forKey:#"SwitchState"];
}
#end
Your code uses the same key for all the attendees - that is what you should take care of.
Since you obviously set the name and surname for each person (and if we presume that two attendees don't have the same name) you could use this to your advantage.
Change all the #"SwitchState" references
to something like
[NSString stringWithFormat:#"SwitchState_%#_%#",setNom,setCognoms]
This would effectively save the state of the switches for each attendee separately.
Using you're line of thinking, you would need to store 3 keys in NSUserDefaults, one for each person. It would be a mess to use it, for instance, if you have 1000 persons.
I believe the proper way to implement this is by using a Delegate on your GuestInfoViewController.
Here's what I would do:
GuestTableViewController have a list of Person objects, Person object have a BOOL for selected or not.
GuestInfoViewController, reads the BOOL value to show the switch, if the value is changed it fires the delegate and updated the list in GuestTableViewController.
This way, everything is updated and you have all information correct. If you need help doing the delegate, you can find a million examples on Stackoverflow. Or ask and I'll elaborate.
** edit **
When dealing with a simple Person object, you don't even need a delegate, its simplier. Check the project in attach: http://www.brunotereso.net/temp/DelegateProject.zip (Please note this is just a piece of code I've put up to show you how to do it. If you implement something like this, have a look on cellForRowAtIndexPath and use reusable cells)
I am looking for a code that allows me to have my textfield hold/save value as I start typing. The issue I am facing is that UITextField sets to the previously stored value if I choose to exit the view controller without hitting the done key on the keyboard. I want the UITextField to hold/save the value as I start typing so it holds the value even if I exit the view controller.
you can save the textField Value in NSUserDefault as you type. And retrieve from it.
in ViewDidLoad
-(Void) ViewDidLoad{
NSString *textFieldValue = [[NSUserDefaults standardUserDefaults] valueForKey:#"fieldValue"];
myTextField.text = textFieldValue;
myTextField.delegate = self;
}
//UITextField delegate
- (void)textFieldDidEndEditing:(UITextField *)textField{
[NSUserDefaults standardUserDefaults] setValue:myTextField.text forKey:#"fieldValue"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I have not tried this specifically on iOS, but I was able to get similar functionality in a Mac app that I was working on by implementing the NSTextFieldDelegate.
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTextFieldDelegate>
Inside of the AppDelegate.m I implemented this part of the protocol:
-(void)controlTextDidChange:(NSNotification *)obj{
This function would run every time text was entered into the field.
Hope this helps.
So one of the users in here managed to show me how to pass data from a child view controller to a parent view controller via a string.
So now the string is passed, BUT, i want that value to stay displayed on the firstViewController after the app is closed and re-opened.
The value is saved in with NSUserDefaults by the way and with an NSLog i am seeing on the conosole it is saved in the apps folder but that value isnt saved onto the UILabel display.
It only displays it when i put save but then i close and reopen, it dissappears but in an NsLog it is still inside the app but not on display UILabel.
How can i address this ?
On my appDelegate.h i have a
#property (strong, nonatomic) NSString *sharedString;
To pass the secondViewController data to the firstViewController.
In the save method on my secondViewController i have a function related to the
AppDelegate.h declaration which is:
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
apiDelegate.sharedString = self.textFieldData.text;
And in my firstViewController i have a method which display the data from the second
viewController:
-(void) viewDidAppear:(BOOL)animated {
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
self.DisplayData.text = appDelegate.sharedString;
[super viewDidAppear: NO];
Is there something wrong which isnt keeping the data intact after app closes or am
I missing something here ?
So one of the users in here managed to show me how to pass data from a
child view controller to a parent view controller via a string.
First you need to establish some hierarchy as to how you get a childViewController from a parentViewController. One way to pass data from childViewController to parentViewController is using a delegate. The other could be using the KVC/KVO protocol. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
In this you can simply register an observer for the property defined in the childViewController and observe it's changes wherever you want (well, given the hierarchy is satisfied).
To save the value. You can simply save it using NSUserDefaults. I don't see any code in your post but you can simply define a key and save the value with NSUserDefaults using:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:sharedString forKey:#"sharedString"];
NSString *sharedStringFromDefaults = [defaults objectForKey:#"sharedString"];
Also,
AppDelegate *apiDelegate = [[UIApplication sharedApplication]
delegate]
Apple requires you to avoid such references in the application. It only constrains the app. Further, the sharedString is not required to be in the AppDelegate. Otherwise the AppDelegate will be filled with almost every other data structure you have shared in the app.
//add this code when you want to store string
[[NSUserDefaults standardUserDefaults] setObject:self.textFieldData.text forKey:#"sharedString"];
[[NSUserDefaults standardUserDefaults] synchronize];
//and when you want string than
self.DisplayData.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"sharedString"];