I have a UITableView. There are only two sections. In the first section there are only two rows. The first row only holds a switch. If this switch is turned On, the second row shows up.
Now I want to save the status of the switch. For that I use NSUserDefaults which code I've added to the action associated to the switch. The action of the switch (interruptor) looks like that:
-(IBAction)accioInterruptor:(id)sender{
NSUserDefaults *pepe=[NSUserDefaults standardUserDefaults];
[pepe setBool:interruptor.isOn forKey:#"interruptor"];
[pepe synchronize];
NSLog(#"Defaults interruptor %d", [[NSUserDefaults standardUserDefaults]boolForKey:#"interruptor"]);
NSLog(#"Interruptor isOn: %d", interruptor.isOn);
[infoAndSettingsTable beginUpdates];
[infoAndSettingsTable reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
[infoAndSettingsTable endUpdates];}
I seem to be, somehow, able save and restore the switch's state by adding the state in the definition of the cell in the cellForRowAtIndexPath method, like this:
if (indexPath.section==0 && indexPath.row==0){
[cell.contentView addSubview:self.interruptor];
[interruptor setOn:[[NSUserDefaults standardUserDefaults] boolForKey:#"interruptor"]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType=UITableViewCellAccessoryNone;
cell.detailTextLabel.text=#"";
}
I added NSLogs at viewDidLoad and in the switch's actions, which look like this:
NSLog(#" interruptor Defaults at startup %d", [[NSUserDefaults standardUserDefaults]boolForKey:#"interruptor"]);
NSLog(#"interruptor isOn? %d", interruptor.isOn);
Now, the problem I'm having here is that when the switch is saved as "ON", the second row on the first section does not appear, it only shows the first row with the switch. If I turn the switch off and on again, then is OK. What I also don't understand is that the NSLogs at viewDidLoad, when the switch is saved as ON, show this:
interruptor Defaults at startup: 1
interruptor isOn:0
which I cannot understand how's that possible. Apparently, the saved state is ON, it's shown as ON (blue color) but when asked, the switch says it's OFF.
I though that would be a easy thing to do, but I've been having two hell of nights. Any help would be much appreciated.
Thanks in advance!
OK, never mind. I got it to work.
I previously tried to add
switch.on=[[NSUserdefaults standardUserDefaults] boolForKey:#"switch"];
to the viewDidLoad but it didn't work.
As it turns out, I was missing a self. before the switch.on.
It now works like a charm.
Related
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)
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 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;
}
}
I am using NSUSerDefaults to store a couple strings and integers for my application. Whenever a view is opened, the string is loaded slower than the view so you see a glitch. For example, I save the selectedSegmentIndex and then read it in viewDidAppear and for a quick moment when the view is called, no segment is selected, then the right one selects. How do you make it so there is no time gap between the view being opened and the setting be read?
- (void)viewDidLoad
{
[super viewDidLoad];
int segmentIndex = [[NSUserDefaults standardUserDefaults] integerForKey:#"selectedIndex"];
unitSegmentControl.selectedSegmentIndex = segmentIndex;
BOOL location = [[NSUserDefaults standardUserDefaults] boolForKey:#"locationManager"];
[gpsSwitch setOn:location animated:NO];
deviceID.text = [[NSUserDefaults standardUserDefaults] stringForKey:#"DeviceID"];
}
- (IBAction)changeSeg:(id)sender {
if (unitSegmentControl.selectedSegmentIndex == 0) {
[[NSUserDefaults standardUserDefaults] setObject:#"http://98.246.50.81/firecom/xml/units/E01.xml" forKey:#"parserURL"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Main" forKey:#"selectedStation"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Fire & Rescue" forKey:#"selectedDepartment"];
}
if (unitSegmentControl.selectedSegmentIndex == 1) {
[[NSUserDefaults standardUserDefaults] setObject:#"http://98.246.50.81/firecom/xml/units/E02.xml" forKey:#"parserURL"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Witch Hazel" forKey:#"selectedStation"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Fire & Rescue" forKey:#"selectedDepartment"];
}
[[NSUserDefaults standardUserDefaults] setInteger:unitSegmentControl.selectedSegmentIndex forKey:#"selectedIndex"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
The defaults are not slow, you’re just loading the data too late. The standard place to populate views is in -viewDidLoad or -viewWillAppear in the view’s controller. Both will update the view soon enough to avoid visual glitches. If any of the two doesn’t work for you, here’s some tips to find the reason:
Try to set the selected index to a hard-wired number. This will tell you if the problem is in the defaults or (much more likely) in the -setSelectedSegmentIndex call.
Move the UI population code to -viewWillAppear. That’s the latest moment to update the UI before it’s displayed.
Use NSParameterAssert to make sure unitSegmentControl is not nil.
Make sure the index read back from the defaults is the expected number. Generally, it’s best to pull the defaults keys into constants. That way you can’t bump into simple typo bugs:
static NSString *const SelectedSegmentKey = #"selectedSegment";
If everything else fails, use a custom UISegmentControl subclass for your unitSegmentControl and place a breakpoint into -setSelectedSegmentIndex to see who else might be calling it.
i am having trouble trying to call values from two IASKCustomViewSpecifier in the InAppSettingsKit. I tried changing the Key value from customCell but it seams to then change the dimensions of the customCell text field. They appear fine in the setting bundle in the simulator and the device how ever i just cant work out how to call each one independently
eg
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
oneString = [defaults objectForKey:#"customCellOne"];
twoString = [defaults objectForKey:#"customCellTwo"];
Plist File contents
Item 0
-Title: One
-Type: IASKCustomViewSpecifier
-Key: customCell
-DefaultValue: Test Example String here
Item 1
-Title: Two
-Type: IASKCustomViewSpecifier
-Key: customCell
-DefaultValue: Test Example String here
Any help would be great,
thanks,
I think my answer over at Github solved the issue: https://github.com/futuretap/InAppSettingsKit/issues/closed#issue/43
I have figured out how to set different IASKCustomViewSpecifier cells. I changed the keys on them and they are all looking good now but the problem i am facing now is that when i get out of the settings bundle it doesnt save the changes I made on the IASKCustomViewSpecifier cell's all the others it works fine. I think it has something to do with this code
- (void)textViewDidChange:(UITextView *)textView {
[[NSUserDefaults standardUserDefaults] setObject:textView.text forKey:#"customCell"];
[[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged object:#"customCell"];
}
Now that all the keys are different not sure what to do with the forKey#"customCell".