How do I get the text written in UITextView/UITextField automatically saved? - objective-c

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

Related

How to load the user’s last selected value for a slider when the app relaunches?

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"];

NSUserDefaults Reset First Run

I have an app already in the AppStore that uses NSUserDefaults. Some of the defaults are Default settings that I go ahead and set when the app is first launched, and then the user is allowed to change them later if they wish. So, in my AppDelegate appDidFinishLaunchingWithOptions I put:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (! [defaults boolForKey:#"notFirstRun"]) {
[defaults setBool:YES forKey:#"notFirstRun"];
[defaults setInteger:0 forKey:#"verseKey"];
[defaults synchronize];
}
The issue I am having now is I want to add some more Default settings in the NSUserDefault category, so I want to make it look like this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (! [defaults boolForKey:#"notFirstRun"]) {
[defaults setBool:YES forKey:#"notFirstRun"];
NSString *smalltitle = #"4";
NSString *smallarticle = #"3";
[defaults setObject:smalltitle forKey:#"Title"];
[defaults setObject:smallarticle forKey:#"Article"];
[defaults setInteger:0 forKey:#"verseKey"];
[defaults synchronize];
}
I know that this will cause an issue for those who have already downloaded the app, and are merely updating it. They will not run that code because the notFirstRun Bool has already been set to YES. Any thoughts on what I should do here?
The proper solution is to not actually populate NSUserDefaults with default values. Instead, use the registerDefaults: method.
At app startup you do:
NSUserDefaults *default = [NSUserDefaults standardUserDefaults];
[defaults registerDefaults:#{
#"Title" : #"4",
#"Article" : #"3",
#"verseKey" : #0
}];
That's it. Call this every time the app is run. These defaults are not actually persisted. The value is only returned if there isn't already an explicit value for the key. You can update these defaults all you want without affecting any existing values.
Make a new notFirstRun Boolean value (i.e. notFirstRunTwo). That will be 'NO' for existing users, too.
I suggest to do the following :
check userdefaults for stored app version if it is equal to the current or not , if not.
store the current app version and do your first launch initialization
the code
`
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *appVersion = [NSString stringWithFormat:#"%#",[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"]];
if (! [defaults objectForKey:appVersion ])
{
/// store the current version and then do your first run functions
[defaults setObject:[NSNumber numberWithInt:1] forKey:appVersion];
/// here do your first run
......
}`

Checking if view controller has been loaded before

I am trying to checking if a certain view has been loaded ever before in the lifetime of the app. I have implemented the following code but not quite sure why it isn't working. It is being done in the viewDidLoad method only of the view controller (perhaps this is the problem). If anyone can let me know what my mistake is, it would be appreciated. Thanks!
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL hasRunBefore = [defaults boolForKey:#"FirstRun"];
if (hasRunBefore) {
NSLog (#"not the very first time this controller has been loaded");
[defaults setBool:YES forKey:#"FirstRun"];
[defaults synchronize];
}
else if (!hasRunBefore) {
[defaults setBool:NO forKey:#"FirstRun"];
NSLog (#"the very first time this controller has been loaded");
[defaults synchronize];
}
There are a couple of mistakes in your code. See the corrections below:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL hasRunBefore = [defaults boolForKey:#"FirstRun"];
if (hasRunBefore) {
NSLog (#"not the very first time this controller has been loaded");
/* Toggle the boolean state */
[defaults setBool:NO forKey:#"FirstRun"];
}
else if (!hasRunBefore) {
/* Toggle the boolean state */
[defaults setBool:YES forKey:#"FirstRun"];
NSLog (#"the very first time this controller has been loaded");
}
[defaults synchronize];
However, I am not sure why you would want to toggle the FirstRun back to NO in the if(hasRunBefore). I only point out what is wrong, you might want to check your logic again.
-EDIT-
Like I pointed out about your logic, you should only have this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL hasRunBefore = [defaults boolForKey:#"FirstRun"];
if (!hasRunBefore) {
[defaults setBool:YES forKey:#"FirstRun"];
[defaults synchronize];
NSLog (#"the very first time this controller has been loaded");
}
else
NSLog (#"Not the first time this controller has been loaded");
The values set in defaults are persistent for as long as the app is installed on the device.
You should think about using a BOOL value set in the app delegate (which does not persist from run to run)
OR
You should set "FirstRun" back to NO in your app delegate in the following method.
- (void)applicationWillTerminate:(UIApplication *)application
NSUserDefaults is a possibility, but you could also use a static BOOL and set it to YES when the view controller is loaded for the first time.

NSUserDefaults not saving in table cell

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"]);

if statement executes before its conditional variables are set

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).