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.
Related
I placed a BOOL inside a UIButton. If I click on the Button once, then I want that BOOL to always be saved so I never has to click on button again while in the game. I used the following code but it doesn't work to save. However, the code should work. It's mainly the final line of code that makes the saving not work. What am I doing wrong?
-(void)viewDidLoad{
[[NSUserDefaults standardUserDefaults] setBool:REDguy forKey:#"save"];
if (REDguy==[[NSUserDefaults standardUserDefaults]synchronize]) {
MAN.image=[UIImage imageNamed:#"RED.png"];
}
REDguy=[[NSUserDefaults standardUserDefaults]boolForKey:#"save"];
}
EDIT:
if (REDguy==YES) {
[[NSUserDefaults standardUserDefaults] setBool:REDguy forKey:#"save"];
REDguy =[[NSUserDefaults standardUserDefaults]boolForKey:#"save"];
MAN.image=[UIImage imageNamed:#"RED.png"];
}
This is an assumption since you didn't really clarify what else is going on in your code, but it looks like your bool REDGuy is always going to be be NO on viewDidLoad, and that if condition is likely to always be false since synchronize will probably always be YES (not to mention, that comparison makes no sense and it's rarely necessary to call synchronize since synchronization with user defaults happens on its own).
A simple way to determine if a button has been pressed before is to add the following to your button action method.
- (void)buttonAction:(id)sender {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"save"];
}
- (void)viewDidLoad {
BOOL hasSaved = [[NSUserDefaults standardUserDefaults] boolForKey:#"save"];
if (hasSaved) {
// You can hide your button here if that's what you're going for
[yourButton setHidden:YES]
}
}
Is that what you are trying to accomplish? Determining if a specific button has been tapped before?
I am wondering if there is a way to maintain user input when navigating back and forth to my main menu using the UINavigationBar back arrow using NSCoder. I am currently using NSCoder to maintain this data during background/terminate/restart and NSUserDefaults to maintain data between views, but the combination of NSCoder and NSUserDefaults is giving unpredictable results. Sometimes, both types of restoration work, but sometimes the BG/Term/RS restoration does not work, and I am talking about loading the same identical code on my device. When the NSUserDefaults code is commented out, the BG/Term/RS restoration works every time.
I would like to know if it is possible to use NSCoder for all of my restoration needs, and if so, what that code would look like.
This is what I am using for BG/Term/RS restoration:
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder {
// start level text
[coder encodeObject:_startLevel.text forKey:#"startText"];
// stop level text
[coder encodeObject:_stopLevel.text forKey:#"stopText"];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder {
// start level text
_startLevel.text = [coder decodeObjectForKey:#"startText"];
// stop level text
_stopLevel.text = [coder decodeObjectForKey:#"stopText"];
}
This is the NSUserDefaults code that I am currently using to persist data back and forth between my menu and main view, and which I would ideally like to replace with an NSCoder solution:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// start level
[[NSUserDefaults standardUserDefaults] setObject:_startLevel.text
forKey:#"startLevelRestore"];
// stop level
[[NSUserDefaults standardUserDefaults] setObject:_stopLevel.text
forKey:#"stopLevelRestore"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// start level
[_startLevel setText:[[NSUserDefaults standardUserDefaults]
objectForKey:#"startLevelRestore"]];
// stop Level
[_stopLevel setText:[[NSUserDefaults standardUserDefaults]
objectForKey:#"stopLevelRestore"]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I have been battling this issue for a couple of days with no luck. Any help would be greatly appreciated! Thanks.
Instead of setting UI objects in decodeRestorableStateWithCoder: use a member variable.
Then in viewWillAppear: use the member variable if it has been set otherwise use the value from NSUserDefault.
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 have a scroll view full of buttons which are also in an NSMutableArray
how can i save the scrollview with the buttons so that they load in view did load.
I have a function that loads the buttons and places them into the scroll view and i have a function that removes the buttons. So it's the end state of the scrollview and its buttons needing to be saved.
If I can just save the array thats ok because I have a createButton function which takes in an NSString and creates a button in the scrollview and adds the object to the array so i suppose i could have 2 arrays but that hasn't worked and I'm fairly new so I think there is probably some really nice way to do this.
Thanks in advance!
Using NSUserDefaults, this is how you would do it. This is how you would save just one array:
[[NSUserDefaults standardUserDefaults] setObject:yourArray forKey:#"SavedArray"];
And then load it:
yourArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"SavedArray"];
Or you could save button locations and button titles separately:
To save...
[[NSUserDefaults standardUserDefaults] setObject:titlesArray forKey:#"Titles"];
[[NSUserDefaults standardUserDefaults] setObject:locationsArray forKey:#"Locations"];
And to load...
titlesArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"Titles"];
locationsArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"Locations"];
NSUserDefaults are actually very cool how simple they are. I hope this has helped.