Looking to understand the iOS UIViewController lifecycle - ios

Could you explain me the correct manner to manage the UIViewController lifecycle?
In particular, I would like to know how to use Initialize, ViewDidLoad, ViewWillAppear, ViewDidAppear, ViewWillDisappear, ViewDidDisappear, ViewDidUnload and Dispose methods in Mono Touch for a UIViewController class.

All these commands are called automatically at the appropriate times by iOS when you load/present/hide the view controller. It's important to note that these methods are attached to UIViewController and not to UIViews themselves. You won't get any of these features just using a UIView.
There's great documentation on Apple's site here. Putting in simply though:
ViewDidLoad - Called when you create the class and load from xib. Great for initial setup and one-time-only work.
ViewWillAppear - Called right before your view appears, good for hiding/showing fields or any operations that you want to happen every time before the view is visible. Because you might be going back and forth between views, this will be called every time your view is about to appear on the screen.
ViewDidAppear - Called after the view appears - great place to start an animations or the loading of external data from an API.
ViewWillDisappear/DidDisappear - Same idea as ViewWillAppear/ViewDidAppear.
ViewDidUnload/ViewDidDispose - In Objective-C, this is where you do your clean-up and release of stuff, but this is handled automatically so not much you really need to do here.

UPDATE: ViewDidUnload was deprecated in iOS 6, so updated the answer accordingly.
The UIViewController lifecycle is diagrammed here:
The advantage of using Xamarin Native/Mono Touch, is that it uses the native APIs, and so it follows the same ViewController lifecycle as you would find in Apple's Documentation.

This is for latest iOS Versions(Modified with Xcode 9.3, Swift 4.1). Below are all the stages which makes the lifecycle of a UIViewController complete.
loadView()
loadViewIfNeeded()
viewDidLoad()
viewWillAppear(_ animated: Bool)
viewWillLayoutSubviews()
viewDidLayoutSubviews()
viewDidAppear(_ animated: Bool)
viewWillDisappear(_ animated: Bool)
viewDidDisappear(_ animated: Bool)
Let me explain all those stages.
1. loadView
This event creates/loads the view that the controller manages. It can load from an associated nib file or an empty UIView if null was found.
This makes it a good place to create your views in code programmatically.
This is where subclasses should create their custom view hierarchy if they aren't using a nib.
Should never be called directly.
Only override this method when you programmatically create views and assign the root view to the view property
Don't call super method when you override
loadView
2. loadViewIfNeeded
If incase the view of current viewController has not been set yet then this method will load the view but remember, this is only available in iOS >=9.0. So if you are supporting iOS <9.0 then don't expect it to come into the picture.
Loads the view controller's view if it has not already been set.
3. viewDidLoad
The viewDidLoad event is only called when the view is created and loaded into memory but the bounds for the view are not defined yet. This is a good place to initialise the objects that the view controller is going to use.
Called after the view has been loaded. For view controllers created in code, this is after -loadView.
For view controllers unarchived from a nib, this is after the view is set.
4. viewWillAppear
This event notifies the viewController whenever the view appears on the screen. In this step the view has bounds that are defined but the orientation is not set.
Called when the view is about to made visible. Default does nothing.
5. viewWillLayoutSubviews
This is the first step in the lifecycle where the bounds are finalised. If you are not using constraints or Auto Layout you probably want to update the subviews here. This is only available in iOS >=5.0. So if you are supporting iOS <5.0 then don't expect it to come into the picture.
Called just before the view controller's view's layoutSubviews method is invoked.
Subclasses can implement as necessary. The default is a nop.
6. viewDidLayoutSubviews
This event notifies the view controller that the subviews have been setup. It is a good place to make any changes to the subviews after they have been set. This is only available in iOS >=5.0. So if you are supporting iOS <5.0 then don't expect it to come into the picture.
Called just after the view controller's view's layoutSubviews method is invoked.
Subclasses can implement as necessary. The default is a nop.
7. viewDidAppear
The viewDidAppear event fires after the view is presented on the screen. Which makes it a good place to get data from a backend service or database.
Called when the view has been fully transitioned onto the screen.
Default does nothing
8. viewWillDisappear
The viewWillDisappear event fires when the view of presented viewController is about to disappear, dismiss, cover or hide behind other viewController. This is a good place where you can restrict your network calls, invalidate timer or release objects which is bound to that viewController.
Called when the view is dismissed, covered or otherwise hidden.
9. viewDidDisappear
This is the last step of the lifecycle that anyone can address as this event fires just after the view of presented viewController has been disappeared, dismissed, covered or hidden.
Called after the view was dismissed, covered or otherwise hidden.
Default does nothing
Now as per Apple when you are implementing this methods you should remember to call super implementation of that specific method.
If you subclass UIViewController, you must call the super implementation of this method, even if you aren't using a NIB. (As a convenience, the default init method will do this for you, and specify nil for both of this methods arguments.) In the specified NIB, the File's Owner proxy should have its class set to your view controller subclass, with the view outlet connected to the main view. If you invoke this method with a nil nib name, then this class' -loadView method will attempt to load a NIB whose name is the same as your view controller's class. If no such NIB in fact exists then you must either call -setView: before -view is invoked, or override the -loadView method to set up your views programatically.
Hope this helped.
Thanks.
UPDATE - As #ThomasW pointed inside comment viewWillLayoutSubviews and viewDidLayoutSubviews will also be called at other times when subviews of the main view are loaded, for example when cells of a table view or collection view are loaded.
UPDATE - As #Maria pointed inside comment, description of loadView was updated

iOS 10,11 (Swift 3.1,Swift 4.0)
According to UIViewController in UIKit developers,
1. loadView()
This is where subclasses should create their custom view hierarchy if they aren't using a nib. Should never be called directly.
2. loadViewIfNeeded()
Loads the view controller's view if it has not already been set.
3. viewDidLoad()
Called after the view has been loaded. For view controllers created in code, this is after -loadView. For view controllers unarchived from a nib, this is after the view is set.
4. viewWillAppear(_ animated: Bool)
Called when the view is about to made visible. Default does nothing
5. viewWillLayoutSubviews()
Called just before the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. Default does nothing.
6. viewDidLayoutSubviews()
Called just after the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. Default does nothing.
7. viewDidAppear(_ animated: Bool)
Called when the view has been fully transitioned onto the screen. Default does nothing
8. viewWillDisappear(_ animated: Bool)
Called when the view is dismissed, covered or otherwise hidden. Default does nothing
9. viewDidDisappear(_ animated: Bool)
Called after the view was dismissed, covered or otherwise hidden. Default does nothing
10. viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
Called when the view is Transitioning.
11. willMove(toParentViewController parent: UIViewController?)
12. didMove(toParentViewController parent: UIViewController?)
These two methods are public for container subclasses to call when transitioning between child controllers. If they are overridden, the overrides should ensure to call the super.
The parent argument in both of these methods is nil when a child is being removed from its parent; otherwise it is equal to the new parent view controller.
13. didReceiveMemoryWarning()
Called when the parent application receives a memory warning. On iOS 6.0 it will no longer clear the view by default.

As of iOS 6 and onward. The new diagram is as follows:

Let's concentrate on methods, which are responsible for the UIViewController's lifecycle:
Creation:
- (void)init
- (void)initWithNibName:
View creation:
- (BOOL)isViewLoaded
- (void)loadView
- (void)viewDidLoad
- (UIView *)initWithFrame:(CGRect)frame
- (UIView *)initWithCoder:(NSCoder *)coder
Handling of view state changing:
- (void)viewDidLoad
- (void)viewWillAppear:(BOOL)animated
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
- (void)viewDidDisappear:(BOOL)animated
- (void)viewDidUnload
Memory warning handling:
- (void)didReceiveMemoryWarning
Deallocation
- (void)viewDidUnload
- (void)dealloc
For more information please take a look on UIViewController Class Reference.

The methods viewWillLayoutSubviews and viewDidLayoutSubviews aren't mentioned in the diagrams, but these are called between viewWillAppear and viewDidAppear. They can be called multiple times.

Haider's answer is correct for pre-iOS 6. However, as of iOS 6 viewDidUnload and viewWillUnload are never called. The docs state: "Views are no longer purged under low-memory conditions and so this method is never called."

There's a lot of outdated and incomplete information here. For iOS 6 and newer only:
loadView[a]
viewDidLoad[a]
viewWillAppear
viewWillLayoutSubviews is the first time bounds are finalized
viewDidLayoutSubviews
viewDidAppear
* viewWillLayoutSubviews[b]
* viewDidLayoutSubviews[b]
Footnotes:
(a) - If you manually nil out your view during didReceiveMemoryWarning, loadView and viewDidLoad will be called again. That is, by default loadView and viewDidLoad only gets called once per view controller instance.
(b) May be called an additional 0 or more times.

Explaining State Transitions in the official doc: https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/index.html
This image shows the valid state transitions between various view ‘will’ and ‘did’ callback methods
Valid State Transitions:
Taken from: https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Art/UIViewController Class Reference_2x.png

As per Apple's doc — Start Developing iOS Apps (Swift)
— Work with View Controllers — Understand the View Controller Lifecycle
viewDidLoad()—Called when the view controller’s content view (the top of its view hierarchy) is created and loaded from a storyboard.
…
Use this method to perform any additional setup required by your view controller.
viewWillAppear()—Called just before the view controller’s content view is added to the app’s view hierarchy. Use this method to trigger any operations that need to occur before the content view is presented onscreen
viewDidAppear()—Called just after the view controller’s content view has been added to the app’s view hierarchy. Use this method to trigger any operations that need to occur as soon as the view is presented onscreen, such as fetching data or showing an animation.
viewWillDisappear()—Called just before the view controller’s content view is removed from the app’s view hierarchy. Use this method to perform cleanup tasks like committing changes or resigning the first responder status.
viewDidDisappear()—Called just after the view controller’s content view has been removed from the app’s view hierarchy. Use this method to perform additional teardown activities.

Related

ViewDidLoad() method called every time in xCode 9.4 and iOS 11

Whenever we present or push from SourceViewController to DestinationViewController, ViewDidLoad() method called every time in DestinationViewController in xCode 9.4 and iOS 11 or later. And when we pop or dismiss from DestinationViewController to SourceViewController, viewDidLoad() method not called.
ViewDidload method only called only when the view loads first time. When you present or push it loads the view so, it calls viewdidload. But when you pop it release the view.
Here is the lifecycle of UIViewController.
ViewDidLoad - Called when you create the class and load from xib. (Either present or push) This method called for initial setup and only one time called.
ViewWillAppear - Called right before your view appears, this will be called every time your view is about to appear on the screen.
ViewDidAppear - Called after the view appears - great place to start
an animations or the loading of external data from an API. This will be also called every time after ViewWillAppear when view appeared on the screen.
ViewWillDisappear/DidDisappear - Same idea as ViewWillAppear/ViewDidAppear only when view is about to dismiss or pop.
ViewDidUnload/ViewDidDispose - In Swift or Objective C, this is where you do
your clean-up and release of stuff, but this is handled automatically
so not much you really need to do here.
Read Apple documentation for details.

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

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

Difference between viewDidLoad and viewDidAppear

What is the difference between viewDidLoad and viewDidAppear? What kind of initialization or custom code goes into those functions?
e.g. presentModalViewController works only when present in viewDidAppear and not on viewDidLoad.
viewDidLoad is called exactly once, when the view controller is first loaded into memory. This is where you want to instantiate any instance variables and build any views that live for the entire lifecycle of this view controller. However, the view is usually not yet visible at this point.
viewDidAppear is called when the view is actually visible, and can be called multiple times during the lifecycle of a View Controller (for instance, when a Modal View Controller is dismissed and the view becomes visible again). This is where you want to perform any layout actions or do any drawing in the UI - for example, presenting a modal view controller. However, anything you do here should be repeatable. It's best not to retain things here, or else you'll get memory leaks if you don't release them when the view disappears.
See: https://developer.apple.com/documentation/uikit/uiviewcontroller
Simply put, you would want to create any controls or arrays in viewDidLoad, where as in viewDidAppear is where you would want to refresh those controls or arrays.
viewDidLoad is called once when the controller is created and viewDidAppear is called each time the view, well, DID appear. So say you have a modal view that you present, when that view is dismissed, viewDidAppear will be called, and viewDidLoad will not be called.

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.

Resources