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)
Related
My UISegmentedControl will not stay selected. I have made sure that momentary is NO. So the solutions I have come across on here have not helped.
Would someone please be able to point me in the right direction?
EDIT
Thought I might make this question a bit clearer.
I have a UISegmentedControl and it has four selections (10,20,30,40) which changes the amount of questions asked on my quiz page. Making a selection works fine and changes the amount of questions.
But when I leave that view and go back later on to change the amount of questions again, it shows the selected as 10 even if I have selected something else.
How can I keep it showing the actual selected value?
EDIT
The number of questions is saved in NSUserDefaults.
[[NSUserDefaults standardUserDefaults] setInteger:amountOfQuestions forKey:#"Amount"]
How do I initialize a segmented control with a value from NSUserDefaults?
EDIT - Solved
#SettingsViewController .m file.
- (void)viewDidLoad
{
amountOfQuestions = [[NSUserDefaults standardUserDefaults] integerForKey:#"Amount"];
if (amountOfQuestions == 10) {
mySegment.selectedSegmentIndex = 0;
}
I did not have the below code in my IBAction for my segmented control. So when i tried the above code it did not work. Now it works a treat.
[[NSUserDefaults standardUserDefaults]synchronize];
#SettingsViewController .m file.
- (void)viewDidLoad
{
amountOfQuestions = [[NSUserDefaults standardUserDefaults] integerForKey:#"Amount"];
if (amountOfQuestions == 10) {
mySegment.selectedSegmentIndex = 0;
}
I did not have the below code in my IBAction for my segmented control. So when i tried the above code it did not work. Now it works a treat.
[[NSUserDefaults standardUserDefaults]synchronize];
I am trying to save/load some data for my game (in this case, the # of tips the player has available to them). If they are playing the game for the first time, they get 10 tips to start with, otherwise, it will load the number of tips they had before they last quit the game. My issue is that when starting the game for the first time, I am setting the number of tips to 10 but the UILabel is displaying 0.
These here are my save and load methods:
- (void)saveSettings
{
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
[settings setInteger:self.tipCount forKey:#"tipCount"];
}
- (void)loadSettings
{
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
if (![settings boolForKey:#"userDefaultsSet"])
{
[settings setBool:1 forKey:#"userDefaultsSet"];
[settings setInteger:10 forKey:#"tipCount"];
self.tipCount = 10;
} else {
self.tipCount = [settings integerForKey:#"tipCount"];
}
}
And this here is how I am updating my label:
- (void)updateLabel
{
Quiz* sharedQuiz = [Quiz sharedInstance];
self.statusLabel.text = [NSString stringWithFormat:#"Tips Remaining: %d", [sharedQuiz tipCount]];
}
Any help that can point me in the right direction is appreciated. Thanks in advance!
Here i would like add few things to your question as precautions, it might be so, you already have been taken that care but still i am trying to say you see below.
Check whether you are calling loadSetting method Before calling any other method (saveSettings method and updateLabel method) by which you want to display some information.look you have not set any value to tips count besides from that single method(loadSetting) if that method not caling it means you are getting this 0 number on UILabel.so you should take care of this single thing whether you have properly called loadSetting method before showing the tips count to the user over the UILabbel.since after looking into your code it seems u that you did everything in correct way as you should have done.
You can check this out but setting some break point at required place and check out the main reason behind it.
I hope it may helps you and could give any idea so that you can get some help form above
Say a user logs in to a certain service in my app. How do I "remember" that the user did this, so it will no longer prompt him/her to do this?
For example, every time the app launches I want it to do something with the user account if they are logged in otherwise, don't do anything. The user logs in from an optional login window they can invoke. Once they login, I'd like to (conceptually at least) set a variable so I can be like if (userIsLoggedIn) { ... } and do things accordingly.
Is this NSUserDefaults? CoreData?
You can easily store it in NSUserDefaults.
// init
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// set the default value
[defaults setObject:#"YES" forKey:#"loggedIn"];
// read value
NSString *loggedIn = [defaults objectForKey:#"loggedIn"]
Update:
Set with BOOL:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"loggedIn"];
read the value:
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"loggedIn"]) {
// go to login screen
} else {
// go to main screen
}
NSUserDefaults exposes your app to security risks if you are using login credentials.
I would recommend using NSKeyedArchiver.
This site has a fantastic simple explanation on how to implement it!
http://haoxiang.org/2011/11/ios-how-to-save-and-load-a-custom-object/
Apple API Doc just for future reference.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSKeyedArchiver_Class/Reference/Reference.html
As the other answers have pointed out NSUserDefaults is useful if you are storing simple non private state.
I generally like to wrap this up with a category so that I can interact with user defaults easier and not have to worry about keys etc.
An example would look like this category
NSUserDefaults+PASAuthorisation.h
#interface NSUserDefaults (PASAuthorisation)
#property (nonatomic, assign, getter=pas_isLoggedIn) BOOL pas_loggedIn;
#end
NSUserDefaults+PASAuthorisation.m
#import "NSUserDefaults+PASAuthorisation.h"
static NSString * const PASLogginInKey = #"PASLogginInKey";
#implementation NSUserDefaults (PASAuthorisation)
- (BOOL)pas_isLoggedIn;
{
return [self boolForKey:PASLogginInKey];
}
- (void)setPas_loggedIn:(BOOL)pas_loggedIn;
{
[self setBool:pas_loggedIn forKey:PASLogginInKey];
}
#end
[[NSUserDefaults standardUserDefaults] setObject:#YES forKey:#"isLoggedIn"];
Valid comments: you can also use setBool:forKey with the same result. Calling synchronize right after this assignment also does not hurt (although it is probably not critical).
Core Data isn't really appropriate for small, unique pieces of data like this.
NSUserDefaults is the right place for small things like this, but if those small things are supposed to be secret -- like login details -- the Keychain is better.
I want to show a help overlay when a user runs my application for the first time.
To do this, I'm using the following code indidFinishLaunching:
if(![[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:#"IPHONEFIRSTRUN"])
[[NSUserDefaults standardUserDefaults]setBool:TRUE forKey:#"IPHONEFIRSTRUN"];
In the view controller, I have:
if ([[NSUserDefaults standardUserDefaults]boolForKey:#"IPHONEFIRSTRUN"]==TRUE) {
[self HelpOverlayIphone];
[[NSUserDefaults standardUserDefaults]setBool:FALSE forKey:#"IPHONEFIRSTRUN"];
}
However, it shows the overlay on the second use as well. How can I fix this? Any help is appreciated.
Your logic is overly complex. You are setting permanently a user default to indicate something happening one time. Instead, in the view controller see if the value is not set, if it is do your action and set the variable so that the code is NOT run again:
if ( ! [[NSUserDefaults standardUserDefaults]boolForKey:#"IPHONEFIRSTRUNCOMPLETE"] ) {
[self HelpOverlayIphone];
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:#"IPHONEFIRSTRUNCOMPLETE"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Take out all the code in the app delegate.
Also it's very likely your original code is not working because you are stopping the app from XCode. If you don't use synchronize user default changes will not be saved in that case (normal quitting of the app does eventually save the changes permanently).
Have you registered the defaults you are using at the start of the program? I would suggest rereading the apple documentation for NSUserDefaults here
You need to first make a call to
- (void)registerDefaults:(NSDictionary *)dictionary
which will only set the key if it doesn't already exist. Then check the key for falseness on the the first run and set it at this point.
My guess is the reason your code isn't working is because the dictionary is never actually being saved in any sort of persistent way.
I have this function which fires when the app fires up- it's always been reliable. I have a uniqueNameOfApp which is just some random hash so that it doesn't collide with other apps.
-(void)loadSettings {
NSMutableDictionary *sttngs = [[NSUserDefaults standardUserDefaults]
objectForKey:uniqueNameOfApp];
if(sttngs != nil) {
[userSettings addEntriesFromDictionary:sttngs];
} else {
_appFiredForFirstTime = YES;
}
}
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