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

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

Related

Code in viewDidLoad runs every time it is called

Hi all I am doing a course in Udemy, and the code calls for placing code in the viewDidLoad function as shown below:
override func viewDidLoad() {
super.viewDidLoad()
placesArray.append(["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"])
}
The array append should only run once, however, when I segue to another viewController and come back, it runs the code to append again. So I now have an array with 2 rows, both of which are Taj Mahal.
I thought that the viewDidLoad function only runs code once?
Is there a way around this?
Thanks.
Addendum:
I am using Swift, so I don't see any alloc and init while creating and launching the viewController. And weird as it sounds, the video tutorial has it working in the viewDidLoad and the trainer is using the storyboard to segue from the initial table view controller to a map view on a view controller and just has a back button on the map view that segue's back to the table view controller via the storyboard as well. - Could be because I have the latest version of the Swift language and the trainer was using an earlier version, (cause I noticed some slight differences in coding earlier) but you never know. Either way whenever he touches the back button it does not run the append code anymore.
I am trying to get in contact with the trainer as some of the suggestions here, though they are good don't seem to work.
I will put the solution in here once I get in contact with the trainer.
The viewDidLoad method is called when your view controller's view finishes loading. The view will load when a view controller's view property is nil and something attempts to access it.
UIViewController *myVC = [[UIViewController alloc] init];
UIView *aView = myVC.view; // this loads myVC's view; viewDidLoad is called when it completes loading.
If the view has unloaded (usually due to memory limitations), it will be called when the view property is next accessed.
Manipulation of data sets should generally not be done within view methods. Consider moving this to the init of the view controller (or to a different "datasource" class).
I suppose you are trying to do data initialisation in viewDidLoad. If there is no other operation on placesArray before viewDidLoad, then instead of append, what about setting the placesArray directly:
placesArray = ["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"]
Then even if your view is unloaded for some reasons. Taj Mahal will still be added once only.
viewDidLoad is called whenever the view controller's view property is set. When does this happen? It depends on how the view controller is contained:
UINavigationController
- View Controller views are loaded as they are added to the navigation stack and "unloaded" (although the viewDidUnload method is deprecated) as they are removed.
UITabBarController
- View Controller views are loaded as they are added to the tab bar regardless of whether they are on screen or not. They stay loaded as you change from tab to tab.
Depending on your needs and use case, you can create your own view controller container that does what you need. Checkout the Apple docs on the proper way to do this:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

Does the viewdidload method get called before the view appear?

I've started learning IOS, I'm wondering does the viewdidload: method get called before the view appears on screen? The apple developer guide says this method gets called after the view is loaded into memory but I don't understand What "loaded into memory" means, does it mean the view doesn't appear on screen?
Does the viewdidload method get called before the view appears?
Yes.
Borrowed from this answer, view controller delegate method order is:
- (void)loadView;
- (void)viewDidLoad;
- (void)viewWillAppear;
- (void)viewDidAppear;
What "loaded into memory" means?
It means when the object (view) is created. It is possible to create and show a view virtually at the same time. However, technically viewDidLoad will be called first.
Yes, it is called before.
ViewDidAppear - it is called when the view is visible to the user, here is the place to start an animation or something like that.
More information about the view controller lifecycle: https://stackoverflow.com/a/5570363/3482000
Yes viewdidload: is called before viewWillAppear:.
and The apple developer guide says this method gets called after the view is loaded into memory means,
The viewController in storyboard/XIB is loading into device memory. Other way we can say the user interface which i design into XIB or storyboard is loding into memory...
Yes, viewDidLoad method is called before viewDidAppear:.
viewDidLoad and viewDidLoad: mean actually different things. You specify : if it has an argument, but viewDidLoad does not, just as a convention.
Loaded in memory means ready to use/display.

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

Custom UIViewController viewDidUnload never gets called

I have a custom UIViewController subclass that handles all the view initialization by itself (it doesn't use nib). There is also another UIViewController subclass loaded from nib. Both are contained by UITabBarController.
When a memory warning comes, the first controller does receive notification, but viewDidUnload doesn't get called. The second controller also receives notification and it's viewDidUnload does get called.
I checked in didReceiveMemoryWarning, self.isViewLoaded is TRUE and self.view.superview is null.
Both controllers (their tabs) are invisible at the time the notification appears.
Is there something special a custom view controller should do to be unloaded in a result of memory warnings?
If you are subclassing UIViewContoller and you do not initialize it from a NIB, you need to subclass the -loadView method. Otherwise iOS assumes that the view cannot be unloaded / reloaded.
It would be sufficient to just add the following to your implementation:
- (void)loadView {
[super loadView];
}
I will try to find a documentation quote for that.
The documentation is unfortunately not very clear on this matter.
I would check out the documentation on the View Controller Lifecycle. Specifically, check out the section on what happens when the memory warning is received. If your custom view controller's view is the view on screen, the OS will not attempt to release this view. Is this view on screen when you're getting the memory warning? In the simulator, navigate to the nib-loaded view and simulate a memory warning, see if your custom view gets released then. Also, see if viewWillUnload is being called. And make sure that in any of these methods that you're overriding that you call super.

viewDidLoad of my RootViewController gets called by application:didFinishLaunchingWithOptions: and applicationDidBecomeActive:

I am using a Storyboard with a UINavigationController that is set as "is initial View Controller". It's attached RootViewControllers. viewDidLoad method gets called after application:didFinishLaunchingWithOptions: and applicationDidBecomeActive:.
Why does that happen?
Because the view is loaded before your application becomes active. Active is defined as the point at which your application begins responding to events, and you need a view for this.
If you want to know why viewDidLoad method gets called after those methed I suggest you to read the app-launch-sequence-ios. It's the normal bootstrap execeution in every iOS application.
When you istantiate a controller with an associated view, the view for that controller takes some time to load in memory. viewDidLoad is not called immediately but only when the view has been loaded completely. Here you are sure that outlet for that view are linked correctly.
Hope it helps.

Resources