I know this has been asked before but I have yet to find a solution to this. I am attempting to save UISwitch state so that no matter which VC I enter, that switch state is still active. However, anytime I leave the VC the switch is in, it's resorts to off. Currently this is the code I'm using to save the switch state:
- (IBAction)tvpSwitch:(UISwitch *)sender {
if (sender.isOn) {
[[NSUserDefaults standardUserDefaults]setObject:#"on" forKey:#"tvpSwitch"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
else {
[[NSUserDefaults standardUserDefaults]setObject:#"off" forKey:#"tvpSwitch"];
[[NSUserDefaults standardUserDefaults]synchronize];
}}
I then put this is any VC viewWillAppear:
-(void)viewWillAppear:(BOOL)animated
{
if ([[[NSUserDefaults standardUserDefaults]valueForKey:#"tvpSwitch"]isEqualToString:#"on"])
{
(sender.isOn=YES);
}
else
{
(sender.isOn=NO);
}}
It also flags in the viewWillAppear method that reads: "Use of undeclared identifier 'sender'."I usually try using the Reference Guide but I'm having a difficult time identifying where this is going wrong. Any help would be great! Thanks!
Like you have an IBAction there, I suppose you created the UISwitch trough Interface Builder. If that's the case, create an IBOutlet from the UISwitch and then always reference to it.
In the first code snippet sender is the parameter passed in the IBAction method which is a reference to the UISwitch.
In the other view controllers you need some reference to that UISwitch but if you want only to check that state without being able to change it in the UI, just get it from NSUserDefaults and use it.
By the way there are designated methods of NSUserDefaults for saving a BOOL type.
- (IBAction)tvpSwitch:(UISwitch *)sender {
[[NSUserDefaults standardUserDefaults] setBool:sender.isOn forKey:#"tvpSwitch"];
}
BOOL switchState;
-(void)viewWillAppear:(BOOL)animated
{
switchState = [[NSUserDefaults standardUserDefaults] boolForKey:#"tvpSwitch"];
// do something with switchState
}
Related
I have a couple of objects stored in the user's NSUserDefaults which I have to use more or less in every single ViewController of my app.
Currently, I basically have the same 3 variables declared, and in the viewDidLoad I initialise them like:
if(....){
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx"] integerValue];
chosenName = [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
}else{
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx_2nd option"] integerValue];
...
}
I'm looking to clean up my code and optimize my code, and I was wondering what the right way to handle a case such like this was, to avoid having these 10-12 exact same lines of code at the start of every single ViewController.
Write an utility class. And create some class methods.
One method can be like,
+ (NSString *)choosenName {
return [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
}
And call the method like,
chosenName = [Your_Utility_Class choosenName];
Yes you can achieve it globally by following simple method.Create NSObject class,please refer my example below.
.h File
//Setting up Session
+(void)SetEmail:(NSString*)value;
+(void)SetFirstName:(NSString*)value;
//Retrieve
+(NSString*)GetEmail;
+(NSString*)GetFirstName;
.m file
+(void)SetEmail:(NSString *)value{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:#"EMAILID"];
}
+(void)SetFirstName:(NSString *)value{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:#"FIRSTNAME"];
}
+(NSString*)GetEmail{
return [[NSUserDefaults standardUserDefaults] stringForKey:#"EMAILID"];
}
+(NSString*)GetFirstName{
return [[NSUserDefaults standardUserDefaults] stringForKey:#"FIRSTNAME"];
}
Now Move to Viewcontroller and access without alloc init as it is in Class method.I am setting up from result.
Setting up in Viewcontroller
[NSDefaultSession SetEmail:#"YourString"];
[NSDefaultSession SetFirstName:#"YourString"];
Now Getting Session from any ViewController
[NSDefaultSession GetEmail]
[NSDefaultSession GetFirstName]
Firstly you should put these values into an object, and secondly use dependency injection.
So first make a class Chosen (for want of a better name) and give it the properties id and name. Now the only thing that needs to worry about where the data is saved and loaded from is the 'Chosen' object, everything else will go through that.
Ok now the dependency injection. You want your VC dependencies to be obvious and clear, don't rely in singletons like NSUserDefaults hidden away inside them. So make .chosen a public property on each of the VC's that needs access to the object.
Init this object in application:application didFinishLaunchingWithOptions: and now inject that into your initial viewController. (ie, set the public property)
Now just pass along the object again by injection to each of the other viewController that needs access to it.
in my option:
create commonClass (sub class of NSObject class)
crate spare methods and use them where u need.
ex:
in ur vc1:
set the values for ur objects
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx"] integerValue];
chosenName = [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
in ur common class:
+(NSString *) chosenID{
NSUserDefaults *serverDefults=[NSUserDefaults standardUserDefaults];
NSString * chosenID =[serverDefults objectForKey:#"chosen_xxxx"];
if (chosenID.length==0) {
// do some actions
}
return chosenID;
}
in another VC2:
NSString *id =[commonClass chosenID];
This is the right way to address this issue. Common functionality is what sub-classing is all about - your view controllers are a specific type of view controller that needs access to these variables.
Create class BaseViewController, which is a sub-class of a UIViewController.
Give BaseViewController two public properties called chosenId and chosenName.
Add the init code you have to the viewDidLoad of BaseViewController.
Remove the init code from each of your existing view controllers.
Make any view controller that requires these variables of type BaseViewController intead of UIViewController.
Those variables are now magically (and consistently) available in all of your view controllers without any code duplication.
In my case there are two ViewControllers. In my first view controller there is a label and when view load, it's text should display as 1. then there is a button, when it click,navigate to second view controller. In the second view , there is a stepper and label. If user tap + ,second view's label text change from 1 to 9,for the - also same(decrease the value). in the second view also there is a button.when it click, second view dismiss (from the first view to second I used presend Modally kind segue with over Current Context presentation.that means when dismiss this secondview, firstview does not load again,it exists in the background). so what I want is to send the second view's label text (after changed by the stepper), as first view's text and update the first view's label.(think if the second view's label text is 3, first view's label text should update from 1 to 3 ). I tried with NSUserdefaults.this is my code.
this is my second view controller
- (void)viewDidLoad {
[super viewDidLoad];
//set default value for adult label
NSUInteger defaultAdultVal = self.adultstepper.value;
self.adultcountLabel.text = [NSString stringWithFormat:#"%d", defaultAdultVal];
}
- (IBAction)adultcountAction:(UIStepper *)sender {
NSUInteger adultVal = sender.value;
self.adultcountLabel.text = [NSString stringWithFormat:#"%d", adultVal];
NSString *adultCount = [NSString stringWithFormat:#"%d", adultVal];
[[NSUserDefaults standardUserDefaults] setObject:adultCount forKey:#"adultcount"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (IBAction)DoneAction:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
and this is my second view controller
- (NSString *)testingAsign
{
NSString *adltCount = [[NSUserDefaults standardUserDefaults] objectForKey:#"adultcount"];
return adltCount;
}
I'm getting the value with this method in the first view, and I want to update first view's value but it didn't work.
There are many ways to this are
By using protocol-delegate - Perfect way
proper and perfect way it to create protocol for it in secondVC, and add one weak property as delegate, and while presenting secondVC.. assign firstVC as delegate of secondVC. Also, Implement that protocol in firstVC. Now when you are dismissing secondVC, call the method in protocol. And implemented method in firstVC get called.. so you get the value there.
By using NSNotification
You can add observer for notification in firstVC and postNotification from secondVC. But this is not proper way.. as firstVC continuously observes for notification. (Don't forget to remove observer.. once you dont require observation)
By using Global variable
You can add one global variable in appDelegate, and assign its value from secondVC. And access that value from firstVC. This is also not proper way. Because that variable always remain in memory.
Trying adding value from userdefault in view will appear in first view controller when second view controller dismiss after setting value in NSUserDefaults.
- (void) viewDidLoad:(BOOL)animated{
[super viewDidLoad:animated];
[[NSUserDefaults standardUserDefaults] #"1" forKey:#"adultcount"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
myStaticLabel.text = testingAsign;
}
- (NSString *)testingAsign
{
NSString *adltCount = [[NSUserDefaults standardUserDefaults] objectForKey:#"adultcount"];
return adltCount;
}
this can be done with so many ways like using delegate methods coredata , nsnotifications and nsuserdefaults. from all these , one of the easiest way to pass data backward, we can easily use NSUserDefaults. this is a sample project of passing data backwark using nsuserdefaults. use this github project and give it a try. project url : pass data backward using NSUserDefaults in ios, objective C
You don't need NSUSerDefaults for this. If all you want to do is to be able to transfer data on segue, you need to use the prepareForSegue function. Check out https://stackoverflow.com/a/7865100/2465172
In which method are you updating in FirstViewController, ViewDidLoad or ViewWillAppear Method? Do [[NSUserDefaults standardUserDefaults] #"1" forKey:#"adultcount"];[[NSUserDefaults standardUserDefaults] synchronize]; in SecondViewController and when you dismiss SecondViewController,get updated value from NSUserDefaults in ViewWillAppear Method of FirstViewController.Hope it will work.
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 looking for a code that allows me to have my textfield hold/save value as I start typing. The issue I am facing is that UITextField sets to the previously stored value if I choose to exit the view controller without hitting the done key on the keyboard. I want the UITextField to hold/save the value as I start typing so it holds the value even if I exit the view controller.
you can save the textField Value in NSUserDefault as you type. And retrieve from it.
in ViewDidLoad
-(Void) ViewDidLoad{
NSString *textFieldValue = [[NSUserDefaults standardUserDefaults] valueForKey:#"fieldValue"];
myTextField.text = textFieldValue;
myTextField.delegate = self;
}
//UITextField delegate
- (void)textFieldDidEndEditing:(UITextField *)textField{
[NSUserDefaults standardUserDefaults] setValue:myTextField.text forKey:#"fieldValue"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I have not tried this specifically on iOS, but I was able to get similar functionality in a Mac app that I was working on by implementing the NSTextFieldDelegate.
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTextFieldDelegate>
Inside of the AppDelegate.m I implemented this part of the protocol:
-(void)controlTextDidChange:(NSNotification *)obj{
This function would run every time text was entered into the field.
Hope this helps.
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.