A UIlabel issue: Keep the string value after app is closed? - ios

So one of the users in here managed to show me how to pass data from a child view controller to a parent view controller via a string.
So now the string is passed, BUT, i want that value to stay displayed on the firstViewController after the app is closed and re-opened.
The value is saved in with NSUserDefaults by the way and with an NSLog i am seeing on the conosole it is saved in the apps folder but that value isnt saved onto the UILabel display.
It only displays it when i put save but then i close and reopen, it dissappears but in an NsLog it is still inside the app but not on display UILabel.
How can i address this ?
On my appDelegate.h i have a
#property (strong, nonatomic) NSString *sharedString;
To pass the secondViewController data to the firstViewController.
In the save method on my secondViewController i have a function related to the
AppDelegate.h declaration which is:
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
apiDelegate.sharedString = self.textFieldData.text;
And in my firstViewController i have a method which display the data from the second
viewController:
-(void) viewDidAppear:(BOOL)animated {
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
self.DisplayData.text = appDelegate.sharedString;
[super viewDidAppear: NO];
Is there something wrong which isnt keeping the data intact after app closes or am
I missing something here ?

So one of the users in here managed to show me how to pass data from a
child view controller to a parent view controller via a string.
First you need to establish some hierarchy as to how you get a childViewController from a parentViewController. One way to pass data from childViewController to parentViewController is using a delegate. The other could be using the KVC/KVO protocol. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
In this you can simply register an observer for the property defined in the childViewController and observe it's changes wherever you want (well, given the hierarchy is satisfied).
To save the value. You can simply save it using NSUserDefaults. I don't see any code in your post but you can simply define a key and save the value with NSUserDefaults using:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:sharedString forKey:#"sharedString"];
NSString *sharedStringFromDefaults = [defaults objectForKey:#"sharedString"];
Also,
AppDelegate *apiDelegate = [[UIApplication sharedApplication]
delegate]
Apple requires you to avoid such references in the application. It only constrains the app. Further, the sharedString is not required to be in the AppDelegate. Otherwise the AppDelegate will be filled with almost every other data structure you have shared in the app.

//add this code when you want to store string
[[NSUserDefaults standardUserDefaults] setObject:self.textFieldData.text forKey:#"sharedString"];
[[NSUserDefaults standardUserDefaults] synchronize];
//and when you want string than
self.DisplayData.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"sharedString"];

Related

iOS - handle global app variables from NSUserDefaults

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.

Objecitive C - How to send SecondVC label text to FirstVC and update the FirstVC label text

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.

Edit a UILabel from another Class (another ViewController)

I want to edit a UILabel which is in ViewContrller2 from ViewController1.
This is my code, but it is not working:
ViewController1.m:
// ....
// In my viewDidLoad :
ViewController2 *vc = [[ViewController2 alloc]init];
// calling a function :
[vc updateLabel];
// ....
ViewController2.m:
// ....
-(void)updateLabel{
self.MyLabel.text = #"Text";
// MyLabel is already declared in ViewController2.h
}
// ....
Please can you help me?
I've tried many codes, but it's still not working and I don't know where the problem is.
Rightly or wrongly, to achieve this I would probably use NSUserDefaults to pull info between View Controllers.
ViewController1
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
[standardDefaults setObject:#"This is my Label" forKey:#"labelKey"];
ViewController2
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
MyLabel.text = [standardDefaults stringForKey:#"labelKey"]
Answer updated to reflect amended question for UIProgressView as per below comment:
ViewController1.m
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
[standardDefaults setDouble:0.75 forKey:#"ProgressValue"]; //value you want your progress view to show
ViewController2.h
...
create an outlet for your progress view here and link it up in IB
#property (weak, nonatomic) IBOutlet UIProgressView *ProgressView;
ViewController2.m
#synthesize ProgressView;
...
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
double ProgressValue = [standardDefaults doubleForKey:#"ProgressValue"];
ProgressView.progress = ProgressValue;
You're creating a new object of ViewController2 class,
if you want to set this value to all views you can use:-
1-NSUserDefaults
2-SQLite
3-Core Data
look to how pass data between view controllers :-
Passing Data between View Controllers
You are creating a new version of vc2, you need to access the one that already exists. Try sharing a reference to the vc2 controller in vc1 instead
You also need to think about whether you should update it directly from vc1 - you could refresh the label on viewDidLoad in vc2 instead
There are a number of options depending on how you have created vc1 & vc2, and there's a good description of the pros and cons here http://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/
Make sure that before calling updateLabel on ViewController2 the viewDidLoad method of ViewController2 is called otherwise self.MyLabel will be nil and hence anything you assign to self.MyLabel.text will be useless. Now to ensure that viewDidLoad of ViewController2 gets called you need to access the view property of ViewController2 since viewDidLoad of a view controller is invoked when first time the view of the controller is tried to accessed.
Once viewDidLoad the things you intend to do will work for sure.
To confirm that this is indeed the problem, keep a breakpoint in the -(void)updateLabel method and analyze the to see if self.MyLabel is nil or not.

Pass data between 2 views without segues

I have 2 views, a login view and a main view.
I use SWRevealViewController, and I want automatically display my menu at the startup of the app. I know how display my menu but I don't know how display it just once at startup.
I want to pass a simple String between my Login view and my Main view but without segue, and made a simple test :
if (myPreviousView == "LoginView")
{
// display my menu
}
Another method would be to use NSUserDefault to store your string, which than can be accessed from anywhere within the application.
So, you put your string into NSUserDefaults in your first view:
// Initialize the NSUserDefaults object and an array for data storage
NSUserDefaults *defsData = [NSUserDefaults standardUserDefaults];
NSMutableArray *myArray = [[NSMutableArray alloc] init];
// Add your string to the custom array
NSString *myString = #"My string.";
[myArray addObject:myString];
// Put the array back into UserDefaults for later use
[defsData setObject:myArray forKey:#"Key"]; // Key can be anything
[defsData synchronize];
Now, the array (and the string in it) is available anywhere. So, when you navigate to your second view controller, just initialize an NSUserDefaults and access the string:
NSUserDefaults* defsData = [NSUserDefaults standardUserDefaults];
NSArray *myArray = [defsData objectForKey:#"Key"];
NSLog("This is my stored string: %#", [myArray objectAtIndex:0]);
You can modify the init method of your second view controller to take a custom attribute when you subclass it. So, lets say you created a standard UIViewController (.h and .m files). You can modify the init method of this new class to your liking in the .h file:
- (instancetype)initWithString:(NSString *)string;
And then replace the standard init with the new one in the .m:
- (instancetype)initWithString:(NSString *)string {
}
So, when you call your view controller into existence, you just use this new init method and pass the string you wanted like this:
UIViewController *viewController = [[UIViewController alloc] initWithString:myString];
[self presentViewController:viewController animated:NO completion:nil];
This is a programmatical approach of course, but it should be applied to interface builder easily (unfortunately, as I never use interface builder, I don't know how exactly, but as I said, it should be fairly straightforward to anyone who uses it).

Xcode ios Background image on multiple views

I recently found how to program a switch and how to change the background from 1 view.
My question is how to change the background of multiple views (UIImage) when changing the value of the switch.
For example: It's a preference page and when I switch the background of the preferences page changes so the switch works.
Now i want the background (UIImage) of my first view named "viewcontroller" to change to the same background as the background of my preferences page.
in my opinion, there may be a lot of different ways to achieve this. What i would do is save the image name string into [NSUserDefaults standardUserDefaults].
Such as when the switch value changes, you call
[[NSUserDefaults standardUserDefaults] setObject:#"BackgroundImageName" forKey:#"BackgroundImageOne.jpg"];
[[NSUserDefaults standardUserDefaults]synchronize];
and when you set up the background, you could do
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
UIImage *bgImage;
if ([userDefault objectForKey:#"BackgroundImageName"])
bgImage = [UIImage imageNamed:[userDefault objectForKey:#"BackgroundImageName"]];
else
bgImage = [UIImage imageNamed:#"DEFAULT_NAME"];
//... Set the bgImage to your background image view ...//
Maybe you have to do some changes based on the actual usage. But hope this give you some hints.
One of possible solutions would be to create a public property of application delegate holding your background image.
#property (strong) UIImage *globalBackground;
In your - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions you should initialize it either to some default image or last saved image.
self.globalBackground = [UIImage imageNamed:#"defaultBackground"];
Let's say your appDelegate header file is MyAppDelegate.h. You should import it in all the classes that use this default background image mechanism. Or simply add it to your Prefix.pch file.
In your settings view controller you would then set:
//your background setting handler
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.globalBackground = ... //however you get the selected image
//and here you set the background image of settings view controller
Then in each of the view controllets viewWillAppear: you set its background in similar manner:
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
//then you use appDelegate.globalBackground the same way you are doing it now
If you have a lot of view controllers you might want to consider creating a subclass of UIViewController that implements this mechanism (possibly calling it UIViewControllerWithBackground) and make all your view controllers subclasses of this class.
Note A: one would (instead of appDelegate's property) typically use a singleton class holding all the settings. It might be considered as a bad (quick & dirty) practice. More...
Note B: for saving the name of last selected background you could use NSUserDefaults. This is out of the scope of this question and there is a lot of sample code available out there...
Is it possible to pass the image, which I saved in Xcode, to a next viewcontroller? Or give it a number and pass that number to a next viewcontroller? For example, when switchone is on, NSNumber = 1, pass it to the next view, and then if (NSNumber == 1) than display "backgroundone.jpg"???

Resources