I have an if statement that's condition is passed to this implementation file via NSUserDefaults as seen below.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *code = [defaults objectForKey:#"codeKey"];
selectedCodeLocal = code;
After this code to retrieve the string variable, I have an if statement:
if (selectedCodeLocal == #"1")
textView.text = "#blah blah blah";
else
textview.text = "#abcdefghijklmnop";
When I build and run, it appears that the variable IS being passed, but it's not being passed until AFTER the if statement executes.
I have places NSLog's around this code that return my selectedCodeLocal string variable and the variable's value is always one step behind. (For instance if I first pass it as 4, then pass it as 1, it will be returned in the log first as 1, then as 4, then as 1) Sorry if I've confused you with that.
UPDATE:
- (void)viewDidLoad {
[super viewDidLoad];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults synchronize];
selectedCodeLocal = [defaults objectForKey:#"codeKey"];
NSLog(#"set: %#",selectedCodeLocal);
self.navigationItem.title = selectedCodeLocal;
[textView setClipsToBounds:NO];
[textView setEditable:NO];
[textView setFrame:CGRectMake(20, 100, 50, 50)];
if ([selectedCodeLocal isEqualToString:#"100"])
textView.text = #"abc";
else
textView.text = #"xyz";
}
The NSLog still displays the old value of selectedCodeLocal.
UPDATE: Here's where that Key is set. (in the previous View)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the selected code
NSString *selectedCode = nil;
if(searching)
selectedCode = [copyListOfItems objectAtIndex:indexPath.row];
else {
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"codesKey"];
selectedCode = [array objectAtIndex:indexPath.row];
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:selectedCode forKey:#"codeKey"];
[defaults synchronize];
}
#Firoze Lafeer: Does this answer your question?
when you change the value in the NSUserDefaults, do synchronize. For example
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:#"4" forKey:#"codeKey"];
[defaults synchronize];
also
if (selectedCodeLocal == #"1")
should really be
if (selectedCodeLocal isEqual:#"1")
With a storyboard, and a segue attached to a tableview cell, your tableView:didSelectRowAtIndexPath will be called after the new view controller is loaded and pushed.
So in other words, you are setting this key in your user's defaults after you have already read the value.
The right time to set up any data you need for the detail view controller is in the prepareForSegue:sender: method on your tableview controller. tableView:didSelectRowAtIndexPath: is too late if you are using a segue on a tableview in a storyboard.
Other thoughts:
Everyone else is right that you should be using isEqualToString:, not ==. The fact that the latter is working for you is really an accident of implementation. You need to do the right thing and not depend on that. Using '==' (which is pointer comparison in this case) is wrong.
Speaking of doing the right thing, you should consider if selectedCode really belongs in your user's preferences (NSUserDefaults). It would be much cleaner to just make that a #property of the detail view controller and set that property directly in your prepareForSegue:sender: method.
Hope that helps.
I think your problem is that old favourite, string comparison.
I think you mean:
if ([selectedCodeLocal isEqualToString:#"1"])
or something like it (it's been over a year since I've written Obj-C).
Related
I am new to Xcode and I am using Objective C in OSX.
I am trying to load the user’s last selected value for a slider when the app relaunches.
my code for the slider is…
- (IBAction)sliderChanged:(id)sender {
amount = [self.amountSlider integerValue];
NSString *amountString = [NSString stringWithFormat:#"%ld", amount];
[self.amountLabel setStringValue:amountString];
}
I have this setter…
- (void)setInteger:(NSInteger)valueforKey:(NSString *)defaultName{
[[NSUserDefaults standardUserDefaults] setObject:#"amountString" forKey:#"amountSlider"];
}
I would like to know how to code the getter.
A step-by-step instructions with code will be appreciated.
Any improvements/corrections to my existing code above would also be appreciated.
Make separate functions for retrieving, saving from defaults like this.
- (void)saveValueToUserDefaults:(NSString *)pValue key:(NSString *)kKey {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[self removeValueFromUserDefaults:kKey];
[defaults setObject:pValue forKey:kKey];
[defaults synchronize];
}
- (void)removeValueFromUserDefaults:(NSString *)kKey {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:kKey]) {
[defaults removeObjectForKey:kKey];
}
}
- (NSString *)retriveValueFromUserDefaults:(NSString *)kKey {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:kKey]) {
return [defaults objectForKey:kKey];
} else {
return kStringEmpty;
}
}
This will make simple to save and retrieve values from defaults.
Like-
[self saveValueToUserDefaults:YOUR_AMOUNT forKey:YOUR_KEY];
and get like this,
YOUR_AMOUNT = [self retriveValueFromUserDefaults:YOUR_KEY];
You asked for it :D
in your class create integer variable called amountChecker = 0
create a function called checker like this :
-(void) checker {
if (amount == amountChecker){
[[NSUserDefaults standardUserDefaults] setObject:amountLabel.text forKey:#"amountSlider"];
}
else
amount = amountChecker;
}
then .. in this function :
- (IBAction)sliderChanged:(id)sender {
amount = [self.amountSlider integerValue];
NSString *amountString = [NSString stringWithFormat:#"%ld", amount];
[self.amountLabel setStringValue:amountString];
[self performSelector:#selector(checker) withObject:nil afterDelay:1];
}
now this code will only store your value if a second passes and your slider didn't change , instead of storing your value like 60 times every second :D
it all starts when you change the slider then after one second it will check if the value changed if not it will store it
and when you reopen the app you will use this code :
amount = [[NSUserDefaults standardUserDefaults] objectForKey:#"amountSlider"];
I have a problem with adding data to an NSMutableArray. I have a tableView and I store the cell text in an NSMutableArray. The tableView is user-populated, meaning that the user enters in what they would like to put on the tableView (to make lists, etc.). The problem I have is with storing the data they have entered. Here is my saving code:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.tasks forKey:#"tasksArray"];
[defaults synchronize];
The variable "tasks" is the NSMutableArray. The above code happens right after the user presses the done button on the keyboard and the keyboard text is added to the "tasks" NSMutableArray. There does not seem to be any problem here because I have debugged and found that everything saves properly.
In my viewDidLoad:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *array = [[NSArray alloc] initWithArray:[defaults objectForKey:#"tasksArray"]];
if ([self.tasks count] == 0) {
NSLog(#"Array is 0");
self.tasks = [[NSMutableArray alloc] init];
}
else {
NSLog(#"Array is > 0");
self.tasks = [[NSMutable array] alloc] initWithArray:[defaults objectForKey:#"tasksArray"]];
}
Now the above code is where I want the data to be loaded onto the array which populates the tableView. The error I get is a NSRangeException, index 0 beyond bounds of empty array. The problem seems to be with the NSMutableArray "tasks" and trying to fill it with the saved data. My goal is to have the "tasks" array populated with the data stored on NSUserDefaults under the key "tasksArray" if the user has previously entered any data. If not, and the tableView should be empty, then I want the "tasks" array to be created.
If any help could be given as to what is wrong with my code or provide a different method of getting a result that would help a lot. Thank you!
Why you are using these If else condition. Just use
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if(!self.tasks)
self.tasks = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"tasksArray"]];
else
[self.tasks addObjectsFromArray:[defaults objectForKey:#"tasksArray"]];
Now you can place the check, where you want
if([self.tasks count]){
}
else{
}
objectForKey returns a generic object. You know it's an array, but NSMutableArray does not recognize it as such. Use either:
self.tasks = [[NSMutableArray alloc] initWithArray:[defaults arrayForKey:#"tasksArray"]];
or
self.tasks = [[NSMutableArray alloc] initWithArray:(NSArray *)[defaults objectForKey:#"tasksArray"]];
I have created about 10-20 UITextViews, but everytime I write something in the simulator, it disappears when exiting the viewcontroller, going back to the "menu" and then clicking back to the viewcontroller with the UITextViews. I really need the text to be saved, but I can't figure out how to do it, despite looking on Youtube etc.
You can have separate save and load method for each uitextview or you can store them in json file. Input : Textfield.text, Section : which textfield do you want to save and load. Then you just call the load method in the ViewDidLoad.
- (void) saveTemporary:(NSString *)input :(NSString *)section
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:input forKey:[NSString stringWithFormat:#"textfield_%#",section]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(NSString *)loadTemporary :(NSString *)section
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *loadingString = [defaults objectForKey:[NSString stringWithFormat:#"textfield_%#",section]];
return loadingString;
}
Here is for the json Tutorial :
http://www.raywenderlich.com/5492/working-with-json-in-ios-5
i`m trying to make an NSMutableArray From NSUserDefaults so i can add/delete and edit it later
my code is
- (void)viewDidLoad {
[super viewDidLoad];
NSUserDefaults *ArrayTable = [NSUserDefaults standardUserDefaults];
[ArrayTable setObject:#"One" forKey:#"myArray"];
[ArrayTable setObject:#"Two" forKey:#"myArray"];
[ArrayTable setObject:#"Three" forKey:#"myArray"];
[ArrayTable setObject:#"Four" forKey:#"myArray"];
[ArrayTable setObject:#"Five" forKey:#"myArray"];
[ArrayTable synchronize];
NSMutableArray *array = [[NSMutableArray alloc] init];
array = [ArrayTable objectForKey:#"myArray"];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [array objectAtIndex:indexPath.row];
return cell;
}
when i build and run nothing shows up
i googled it but with no help, i`m sure i didn't understand how to do it
what i need is to build an app that contain a tableview with empty data, the use will fill in the data Add/Delete/Edit
can someone please explain it for me
thank you in advance
First, you assigned your array from the user defaults to a local variable named array. Assuming you have a property for this class also named array, this local assignment masks that. If it had not, you would have crashed when you tried to call -count on a string.
The NSUserDefaults object is a dictionary. Each time you call -setObject:forKey: on it, you are actually replacing the object previously set for that key. So at the end of your series of calls to -setObject:forKey:, the resulting value is the NSString Five.
You can't really store a mutable object in the NSUserDefaults, instead you would take a mutable copy of the object when you assign it to your local variable or ivar. To get the behavior you are probably expecting, you should do something like the following:
- (void)viewDidLoad
{
// Create the array template and store it in NSUserDefaults
NSArray* arrayTemplate = [NSArray arrayWithObjects:#"One", #"Two", #"Three", #"Four", #"Five", nil];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arrayTemplate forKey:#"myArray"];
[defaults synchronize];
// Retrieve a mutable copy of the array from user defaults and assign it to the
// the property 'array' <- note, this should be an NSMutableArray
self.array = [[defaults objectForKey:#"myArray"] mutableCopy]; // if not using arc, autorelease this here
}
With that example code, it should behave the way you expected it to behave, and you can continue on. Obviously it makes no sense to set the array in -viewDidLoad and then immediately read a mutable copy. The key thing to keep in mind is that -setObject:forKey: will always replace any object already set for that key. It doesn't add elements or anything like that.
Your problem is that you are setting a bunch of strings all to the same key:
[ArrayTable setObject:#"yourvalue" forKey:#"myArray"];
just keeps overriding your last value. In order to save an array to your defaults you will have to go about it another way. This question may help you: array to defaults
Since you don't actually want anything to do with the contents of NSUserDefaults, you're just asking how to initialize a mutable array. Here's how to do what you were trying to do:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"One"];
[array addObject:#"Two"];
[array addObject:#"Three"];
[array addObject:#"Four"];
[array addObject:#"Five"];
Or, more simply:
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"One", #"Two", #"Three", #"Four", #"Five", nil];
(These aren't quite identical. If you're using MRC, the first creates an array that you own and will have to release manually, while the second creates an array that's autoreleased. If you don't understand the difference, the second is better until you learn, but soon you should go learn.)
If you want to know why what you were doing didn't work, here goes:
NSUserDefaults *ArrayTable = [NSUserDefaults standardUserDefaults];
Here you're copying an NSUserDefaults object, which is not an array, or a table; it's a wrapper around the user and system preferences that acts like a dictionary with extra functionality.
[ArrayTable setObject:#"One" forKey:#"myArray"];
This line adds a preference named "myArray" to the user-domain preferences for your application, with the value "One".
[ArrayTable setObject:#"Two" forKey:#"myArray"];
[ArrayTable setObject:#"Three" forKey:#"myArray"];
[ArrayTable setObject:#"Four" forKey:#"myArray"];
[ArrayTable setObject:#"Five" forKey:#"myArray"];
These lines change the value of your "myArray" preference repeatedly, ending with "Five".
[ArrayTable synchronize];
This makes sure that your preference is saved to disk.
NSMutableArray *array = [[NSMutableArray alloc] init];
This creates a new mutable array and stores it in the variable "array".
array = [ArrayTable objectForKey:#"myArray"];
This gets the "five" string out of your preferences and stores it into the variable "array".
This means you've now lost the only reference you had to the actual array.
This also means the static type of the variable no longer matches the dynamic type. When you later call [array count], or [array objectAtIndex:n], you're sending those messages to a string, not an array, so you're probably going to get an exception or other unexpected behavior. (Well, it's pretty much guaranteed that whatever you get is going to be unexpected, since you thought you were talking to an array of 5 objects, not a string.)
Create the array and add that to user defaults.
- (void)viewDidLoad {
//...
NSArray * tmpArray = [NSArray arrayWithObjects:#"One",#"Two",#"Three",#"Four", #"Five", nil];
NSUserDefaults *ArrayTable = [NSUserDefaults standardUserDefaults];
[ArrayTable setObject:tmpArray forKey:#"myArray"];
[ArrayTable synchronize];
array = [[ArrayTable objectForKey:#"myArray"] mutableCopy];
// array has to be an ivar of your viewcontroller to access it outside of viewDidLoad
// define in your header like #property (nonatomic, strong) NSMutableArray * array;
// with #synthesize array; in the implementation
}
When I replace the #"hello" with titlestring the table, the cell shows up blank, If i leave the hello, everything works, including the "detailsstring". I don't know why it's doing that since detailsstring is set up exactly the same as titlestring.
here is the code:
in the .m file I have
#synthesize detailsstring;
#synthesize titlestring;
and it's all defined the same in the .h in the .m I have this as well:
- (void)viewDidLoad{
titlestring =[[NSUserDefaults standardUserDefaults] objectForKey:#"titletext"];
detailsstring = [[NSUserDefaults standardUserDefaults] objectForKey:#"details"];
tabledata = [[NSArray alloc] initWithObjects:#"hello", nil];
tablesubtitles = [[NSArray alloc] initWithObjects:detailsstring, nil];
[super viewDidLoad];
}
Now, I'm saving those Userdefaults in another controller. Here is the save button action in a different view controller:
- (IBAction)saveButton:(id)sender {
NSUserDefaults *titletext = [NSUserDefaults standardUserDefaults];
[titletext setObject:titleTextfield.text forKey:#"titletext"];
NSUserDefaults *details = [NSUserDefaults standardUserDefaults];
[details setObject:detailstextfield.text forKey:#"details"];
NSUserDefaults *categoryUser = [NSUserDefaults standardUserDefaults];
[categoryUser setInteger:selectedCategory forKey:#"category"];
}
What am I doing wrong?
First of all, one instance of NSUserDefaults is sufficient for all three modifications. To 'push' the changes to the file system immediately, you can call [userDefaults synchronize];. Otherwise there is no guarantee that your changes are saved instantly. Apple suggests only to call this method if you cannot wait for the changes to be saved.
- (IBAction)saveButton:(id)sender {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:titleTextfield.text forKey:#"titletext"];
[userDefaults setObject:detailstextfield.text forKey:#"details"];
[userDefaults setInteger:selectedCategory forKey:#"category"];
[userDefaults synchronize];
}
In your viewDidLoad: method, try to output the saved values to test your application's integrity:
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] objectForKey:#"details"]);