Update NSUserDefaults data in one ViewController changed in other ViewController - ios

I got two viewcontroller: MainController and OptionsController.
OptionsController it can be reached through a button from MainController.
In OptionsController is there some values that I can save with NSUserDefaults. These values are needed in MainController.
If I change these values in OptionsController when I come back to MainController they don’t change, but if I launch MainController again, these values was changed correctly.
It seems that when MainController becomes active again, after leaving OptionsController, viewDidLoad is no longer raised.
How can I update the data in MainController, please?

As rounak says, viewDidLoad is only called once in the life of a view controller. If you have code you want executed every time a view controller is shown, put it in viewWillAppear.
NSUserDefaultsis a fairly heavyweight way to pass info between in-memory objects (it writes to disk). If you don't need persistence between runs, one of the other options he suggested would be better. I'd suggest the delegate pattern or a completion block.

viewDidLoad is called only once for a view controller. You can use NSNotification, KVO, blocks or something like a delegate pattern to get a callback in your MainController whenever the value changes inside the OptionsController.
You can otherwise write code in viewWillAppear which gets called each time you pop the options controller.

Related

Understanding interaction between Swift files - how do I call from one to the other

I have my ViewController.swift which is the first to run when the app loads. I then segue to another SecondViewController.swift file.
The ViewController.swift instance is still in existence, correct?
So now, from SecondViewController.swift, I call ViewController().someFunction()
The ViewController's "viewDidLoad" does not get called, only the function I specifically called to.
Now: if I instantiate an Int variable in ViewController.swift "var testNum = 2" and in the ViewController's viewDidLoad I change its value to 14. Then I segue to SecondViewController. Then I call to ViewController().someFunction() and I print the value of testNum in that function, I get the instantiated value of 2 instead of the value that ViewController had which was 14, which leads me to believe that I've created a new copy of ViewController by calling a function in this way.
Is that right? Is that what I've done? And if so, is there a way to call a function in the existing ViewController instead of creating a new ViewController from within SecondViewController.swift?
THANKS!
I have my ViewController.swift which is the first to run when the app loads. I then segue to another SecondViewController.swift file.
Nitpicking: You don't segue between 'files', you segue between instances of classes defined in those files.
The ViewController.swift instance is still in existence, correct?
Yes. Well, the original instance of the class defined in that file, see above.
So now, from SecondViewController.swift, I call ViewController().someFunction()
The ViewController's "viewDidLoad" does not get called, only the function I specifically called to.
With ViewController() you create a new instance of the ViewController class and then directly invoke that method on it. Which doesn't make a lot of sense.
Now: if I instantiate an Int variable in ViewController.swift "var testNum = 2" and in the ViewController's viewDidLoad I change its value to 14. Then I segue to SecondViewController. Then I call to ViewController().someFunction() and I print the value of testNum in that function, I get the instantiated value of 2
When you call ViewController() you create a new instance of the class defined in the ViewController.swift. Quite (very much) likely this is not what you want to do.
It appears you rather want to hang on to the original instance of that VC. So you need to transfer that when doing the segue.
I'm sure you've read the View Controller Programming Guide for iOS, right? ;-) This is a relevant section:
The prepareForSegue:sender: method of the source view controller lets you pass data from the source view controller to the destination view controller. The UIStoryboardSegue object passed to the method contains a reference to the destination view controller along with other segue-related information.
Usually you wouldn't want to preserve a reference to the original view controller, but rather transfer relevant data from that into the VC you segue to (to decouple them).
If you're going to be storing values, I'd recommend creating a model class.
https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html
This will keep the data independent of the controllers because, to answer your "The ViewController.swift instance is still in existence, correct?" question, it may or may not be in existence depending on memory. That's why creating a model is desirable because then you're not worried about it.
If you'd like examples, there are many online if you google MVC iOS. Hope that helps.

UIViewController stay active in background

I use the following to open a new view controller
ViewController *mainMenu = [self.storyboard instantiateViewControllerWithIdentifier:#"mainview"];
[self.navigationController pushViewController:mainMenu animated:NO];
When the mainview is active on the screen the application still runs old view controller (methods) in the background.
How i can stop the old view controller from running or make it inactive?
First, let's ask ourselves this: what kind of methods run on a view controller?
I'm going to categorize the methods that are part of a UIViewController subclass into 4 categories (though there's clearly overlap among these categories).
Life-cycle methods. These are methods such as viewDidLoad, viewWillAppear:, etc. that run as part of the view controller's life-cycle. None of these methods should be running if the view is "in the background" other than, arguably, viewDidLoad, viewWillAppear: which are technically called before the view has come to the foreground, and viewDidDisappear: which is technically called just after the view has left the foreground. So we don't really need to worry about these.
Delegate methods. Your view controller could delegate objects. The delegate methods are called on your view controller whenever the object they're delegating calls them. In the majority of the cases, we're talking about delegating some sort of UI element which is part of the view controller. This UI element would be "in the background" any time the view controller is, so it shouldn't be calling delegate methods.
NSTimer called methods. NSTimer can keep a strong reference to an object, and keep that object alive until the fire time ticks over and it calls the specified method on that object. These methods are certainly likely to be called even if the view controller is in the background, but if you want to stop the timer from running if the view controller's view isn't visible, then just invalidate it in viewDidDisappear:.
Miscellaneous methods. These methods don't necessarily into any of the above categories (though they could). Most of the time these methods are called are from within the view controller themselves, so if nothing is being called from one of the first three categories, it will be rare that this category is called (unless the method is public).
So you've got methods running "in the background" on your view controller. What category do they fall in?
If they're in the first category, then either you're calling a life-cycle method manually from somewhere (which I can't recommend against enough--there's no good reason to do this), or perhaps you don't understand the UIViewController life cycle.
If they're in the second category, then it's most likely good that they're being called. If the Object A is being delegated by View Controller A and Object A is still working on some process and still requires delegation, then it's extraordinarily important that View Controller A is still responding to the delegation methods. If you don't want this to happen, you need to stop Object A from requiring delegation.
If they're in the third category, invalidate your NSTimer objects when it's appropriate. NSTimer doesn't just magically not do what you scheduled it to do. You have to invalidate it in viewDidDisappear if you don't want it to call methods on the view controller when its view is not visible.
If they're in the fourth category, start by making sure you've not got public methods that you're calling manually from outside the view controller. If you do, well stop calling them. If you don't, go double check the first three categories.
If you mean that the view controller that was active before is still executing code, I'd suggest doing the following to remove it from view and execution before pushing:
ViewController *mainMenu = [self.storyboard instantiateViewControllerWithIdentifier:#"mainview"];
[self.navigationController popViewControllerAnimated: NO]; // Takes the old one away
[self.navigationController pushViewController:mainMenu animated:NO];

Does viewDidLoad get called more than once in UITabBarController?

I'm using SWRevealViewController in my app (to get the slide out side panel) however whenever the user navigates to another viewController like 'settings' and comes back, everything gets reset. I understand this is normal behaviour for storyboards since a new VC is instantiated and viewDidLoad is called each time. I tried to get around this by storing the VC in an array in the AppDelegate and then going back to the original viewController, this prevented viewDidLoad being called when the original VC is initially re-presented but I still found it get's called randomly when moving between veiwcontroller's, resetting all my properties etc.. On researching, the Apple documentation does say not to assume viewDidLoad will only be called once.
Is this behaviour apparent in UITabBarController when switching tabs as I'm thinking of ditching the SWRevealViewController and using that instead if it's going to be less headache.
Should I be handling this differently, ie. storing the 'state' in NSUserDefaults and restoring on viewDidLoad?
Thanks in advance.
viewDidLoad is called exactly once, when the UIViewController is first loaded into memory. This is where you want to instantiate any instance variables and build any UIViews that live for the entire lifecycle of this UIViewController.
In UITabBarController also the viewdidLoad for UIViewController is called once, when you are switching tabs.
viewDidLoad() method is called only once. Its an integral part of the cycle.
It is called then the respective UIViewController class is loaded into memory.
And yes, if you want to initialise any properties or access and modify the NSUserDefaults, it can and should be done in the viewDidLoad method.
As for your app, whenever the user will switch between different UIViewControllers, the viewDidLoad method will be called for every destination UIViewController.
Also, as correctly pointed out, it'll also be called in the case of a memory warning.

About viewController's "viewDidLoad" and "viewWillAppear" methods

I've got a question regarding the two mentioned methods, since in my tests I don´t make clear the order they are called. I thought that, firstly, viewDidLoad is called when the viewController is loaded for first time (as the name indicates), and inmediately after the init method. Then, I thought that once viewDidLoad returns, viewWillAppear is called. If you display another viewController, and then you return to this one, then it should be already loaded and only viewWillAppear will be called.
However, while developing I make the impression that there is no order when calling viewDidLoad and viewWillAppear... I couldn´t find a clear description of this lifecycle in Apple's documentation, how does this actually work?
Thanks!
I would like to add to Caleb's answer: Don't confuse the view controller and the view! The name viewDidLoad clearly indicates that the method is invoked after the view has been loaded. It is the view controller that does the loading.
Some pointers regarding the lifecycle of views and the order in which messages are sent:
Not an official Apple document, but I find this diagram really useful because it includes pretty much all of UIViewController's lifecycle overrides.
In the section Resource Management in View Controllers from Apple's "View Controller Programming Guide" there is a flowchart that depicts how views are initially loaded. It explains loadView and viewDidLoad, also in conjunction with storyboards.
The section Responding to Display-Related Notifications from Apple's "View Controller Programming Guide" explains how to respond to views appearing and disappearing (viewWillAppear: et al)
If you are planning on implementing a container view controller: The UIViewController class reference has a good overview of how messages need to be sent by your subclass.
I'm stopping here. You can find more stuff yourself by googling for "uiviewcontroller life cycle".
-viewDidLoad is called when the controller loads its view, which is not necessarily right after initialization. View controllers don't load their views until they need them, either to display or for any other reason.
-viewWillAppear is called just before the view is displayed. This will be after -viewDidLoad, but you don't know exactly how long after. -viewWillAppear is called every time the view is displayed; -viewDidLoad will only be called a second time if the view is unloaded at some point (such as didReceiveMemoryWarning). These days that's unusual, but it can happen.
Or if the viewController is set to nil, which can usually happen if a view controller is kicked off the navigation stack, and therefore next time it is brought to the navigation stack it needs to call -viewDidLoad again.
I thought that, firstly, viewDidLoad is called when the viewController
is loaded for first time (as the name indicates), and inmediately after the init method
No. The name indicates that the controller's view has been loaded (not the controller itself). Actually the docs state that this method will get called after the view hierarchy has been loaded into memory (either via loadView or through a nib for example).
Then, I thought that once viewDidLoad returns, viewWillAppear is
called
Again, no. loadView (and as a consequence viewDidLoad) method will get called the first time that view property is to be accessed and is nil (which is the case when you're initializing a controller). Think of this simple scenario:
MyViewController *vc = [[MyViewController alloc] init];
UIView *view = vc.view; // <= loadView & viewDidLoad will fire but it certainly didn't appear...
However, while developing I make the impression that there is no order
when calling viewDidLoad and viewWillAppear...
Well there is an order. We know for sure that viewWillAppear will always be called after viewDidLoad (if both of them are to be called of course).
As you said, ViewDidLoad is only calling once after loading the view. So we can initialize the instances in the viewDidLoad. It is mainly meant for the initialization.
viewWillAppear will invoke whenever we reach to this view. So if there is any changes in UI, we can done it in viewWillAppear.
I ran a trace on when all these calls are made: http://thecodist.com/article/ios_arc_storyboards_and_uiviewcontroller_trace

NSNotification called before view controller is initialised completely

Hi I have a viewcontroller in a tabview controller. I decided to use NSNotification to flag when views in the tabview controller need to update their data. Previously each had logic to tred to decided the state of the data model and update accordingly.
My update code calls some stuff that in turn call delegate methods. These were all working when not using notification.
My first attempt at notifying seemed to call the selector before the view controller had initialised (before viewDidload at least). Among other thing the delegate methods were never called when the update in the notification selector was run and the view controller didn't get updated. Seems like the viewcontroller is in some unknown state.
I ran a test and put the same update code in the viewDidLoad and only called the notification code after the viewDidLoad had been called. This works.
My question is ,is there another way of preventing the notification selector method being called before viewDidLoad or the object is otherwise correctly initialised.
I am using storyboard so I am not programming the creation of views etc.
I hope this is clear - posting a whole bunch of code would not had been any easier.
Thanks guys, both right! I set up the Notification in app delegate. I should put it at the end of viewDidLoad. Then it is only called when there is a subsequent update and I don't need a flag to stop the first notification from doing an update before the object is up and running.

Resources