The methods applicationDidBecomeActive, loadView, and viewDidLoad will get called at appropriate times in an iOS app. For loadView and viewDidLoad, it looks like it is:
-(void) someMethod {
//...
[viewController loadView];
[viewController viewDidLoad];
}
Is that how they get called and what is the class that call them? (Is there source code that can show the flow of the starting of an app? A lot of times, we can only see the header files but not the source code).
If I understood well your question, you would like to know about the application lifecycle, is it true?
Well, I guess there is no source code provided by apple that can display you how it looks like.
If you want to know how happens when an application starts, I suggest to read about app-launch-sequence-ios-revisited by Oleb. It's a very good post.
About the methods you wrote, these methods shouldn't not called manually. It's the framework (through the iOS) that calls them for you.
The methods loadView and viewDidLoad are methods that are called during the UIViewController lifecycle.
You use (override) loadView when you cannot create a storyboard or a nib file. In this manner you can provide to your UIViewController a fresh view. From Apple doc:
If you cannot define your views in a storyboard or a nib file,
override the loadView method to manually instantiate a view hierarchy
and assign it to the view property.
In other words:
- (void)loadView
{
UIView* myCustomView = ... // create the view here
self.view = myCustomView;
}
About the viewDidLoad method, this is called when a view has been set up in memory. Once done you are sure that outlets, for example, are set up and you can perform additional initializations.
From Apple doc:
This method is called after the view controller has loaded its view
hierarchy into memory. This method is called regardless of whether the
view hierarchy was loaded from a nib file or created programmatically
in the loadView method. You usually override this method to perform
additional initialization on views that were loaded from nib files.
In other words:
- (void)viewDidLoad
{
[super viewDidLoad];
// additional initializations here
}
Finally, about applicationDidBecomeActive method (or delegate if you want), this is called to let your application know that it moved from the inactive to active state.
I suggest you to read UIApplicationDelegate and UIViewController class references.
If you want to simply verify the sequence call, override the methods and put a NSLog there.
Hope it helps.
Related
Is there a way to accomplish this? Like let's say a framework perhaps?
All I need is something like:
aViewController appeared
bTableViewController appeared
cViewController appeared
Like I added a code in each viewDidLoad, but without adding that code. I know that there is a visibleViewController if I used navigation embed but I don't and I can't.
The framework I'm working on is based on no baseline, so I can not assume anything, like I'll past a code in a project and when the user compiles the code, I will get the views.
The purpose is I'm creating an analytic tool without being have to add individual code in each view controllers's viewDidLoad method.
First of all, there's no such thing as a "visible view controller" technically speaking, despite the annoyingly named method of navigation controllers. Instead, we should be talking about visible views. There's nothing visible about the controller.
But what you really want to know is what view controller is active and controlling visible views. With a navigation controller, it's the top of the nav-stack, with a tab bar controller, it's the selected view controller, etc.
By implementing ONLY code in app-delegate, I don't know of any way to accomplish what you're trying to accomplish.
Instead, what would be your best option would be to provide a framework that included a subclass of UIViewController and UIAppDelegate.
Set the code in these to do all the analytical work necessary. Provide this framework as a whole and inform your users that if they want to make use of the analytics you've provide, they'll need to subclass your view controller instead of UIViewController, etc.
In your custom classes, just override viewDidAppear: to send a notification and viewDidDisappear: to send a notification. And the appdelegate just manages an array that keeps track of which is on top, adding when the viewDidAppear: notification fires, and removing when the viewDidDisappear: notification fires.
And to be sure your viewDidAppear:/viewDidDisappear: isn't overridden, be sure to make use of this: When a subclass overrides a method, how can we ensure at compile time that the superclass's method implementation is called?
So in your view controller base class, it'd be as simple as posting a notification:
// AnalyticViewController.h
#interface AnalyticViewController : UIViewController
- (void)viewDidAppear NS_REQUIRES_SUPER;
- (void)viewDidDisappear NS_REQUIRES_SUPER;
#end
// AnalyticViewController.m
#implementation AnalyticViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AnalyticVCAppeared"
object:self
userInfo:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AnalyticVCDisappeared"
object:self
userInfo:nil];
}
#end
And register for these in the App delegate in application:didFinishLaunchingWithOptions:. You'll want an mutable array to keep track of the stack of view controllers. And you'll want separate methods for each notification.
Given a property #property (nonatomic,strong) NSMutableArray *analyzedViewControllers;, the methods that respond to the notifications should look like this:
- (void)handleViewAppear:(NSNotification *)aNotification {
if (!self.analyzedViewControllers) {
self.analyzedViewControllers = [NSMutableArray array];
}
[self.analyzedViewControllers addObject:[aNotification object]];
}
- (void)handleViewDisappeared:(NSNotification *)aNotification {
[self.analyzedViewControllers removeObject:[aNotification object]];
}
And the top-most view controller will always be at [self.analyzedViewControllers lastObject].
If you need to send more information about the view controller, you can using the userInfo argument when posting the notification. You could even put the entire notification into the array, so you have the view controller reference and the user info reference.
What can also be important to note however is that just because one view controller has appeared doesn't inherently mean another has disappeared. View controllers don't necessarily take up the entire screen, and although there's eventually a practical limit to how many view controllers have visible views on screen, it's a quite high limit.
ADDENDUM: To add some clarity... in the AnalyticViewController class I'm recommending, we have this line:
#interface AnalyticViewController : UIViewController
A line similar to this exists in every one of your view controller's header files. What I'm recommending is that all your other view controllers subclass this one, so instead of:
#interface FooBarViewController : UIViewController
You'd instead have this:
#import AnalyticViewController.h
#interface FooBarViewController : AnalyticViewController
And the code in the viewDidAppear: and viewDidDisappear: of AnalyticViewController is now automatically in FooBarViewController unless you ignore the NS_REQUIRES_SUPER warning and override the viewDidAppear: or viewDidDisappear: methods.
It will be a little bit of effort to change over existing view controllers, but for any future view controllers that are not yet created, it's as simple as choosing to subclass AnalyticViewController rather than UIViewController when you create the file.
For creating a analytical framework i would say you should write a kinda protocol of a rootviewController and viewController class and that delegate methods can be implemented in app delegate for knowing and handling your purpose.
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.
When I program without a nib, I am under the impression that I need to call loadView to initialize my view, like this:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
// Custom initialization
[self loadView];
}
return self;
}
(I have set nibNameOrNil = nil, since there is not nib.)
Then, I set up the view, like this:
- (void) loadView {
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 367)];
[self viewDidLoad];
}
This is do everything else in viewDidLoad.
I'm still unsure if I am supposed to make the calls to loadView and viewDidLoad in this way. They are not getting called automatically.
What is confusing is in the documentation for the UIViewController class reference:
loadView Discussion
You should never call this method directly. The view controller calls
this method when the view property is requested but is currently nil.
If you create your views manually, you must override this method and
use it to create your views. If you use Interface Builder to create
your views and initialize the view controller—that is, you initialize
the view using the initWithNibName:bundle: method, set the nibName and
nibBundle properties directly, or create both your views and view
controller in Interface Builder—then you must not override this
method.
So, I don't understand how loadView gets called if I should never call it directly.
The default implementation of this method looks for valid nib
information and uses that information to load the associated nib file.
If no nib information is specified, the default implementation creates
a plain UIView object and makes it the main view.
I don't understand how that works -- the creation of a pain UIView.
If you override this method in order to create your views manually,
you should do so and assign the root view of your hierarchy to the
view property. (The views you create should be unique instances and
should not be shared with any other view controller object.) Your
custom implementation of this method should not call super.
If you want to perform any additional initialization of your views, do
so in the viewDidLoad method. In iOS 3.0 and later, you should also
override the viewDidUnload method to release any references to the
view or its contents.
Okay, so far it doesn't say how viewDidLoad is called. So for viewDidLoad:
viewDidLoad Discussion
This method is called after the view controller has loaded its
associated views into memory. This method is called regardless of
whether the views were stored in a nib file or created
programmatically in the loadView method. This method is most commonly
used to perform additional initialization steps on views that are
loaded from nib files.
Called by what?
Since These methods are not getting called automatically in my code, I am left to think that I have to call them myself. But I still don't get a clear understanding form the documentation that this is the right thing to do.
As the documentation says, you should not call these methods yourself.
How this is meant to work is that UIKit's code will call your loadView method if and when it actually needs to present that controller's view hierarchy on screen.
Specifically, if any code attempts to read your view property on your view controller, and that property is nil, then UIViewController's code will call the loadView method. If you don't implement loadView yourself, then the default implementation will fall back to attempting to load the view hierarchy from the nib.
This all happens automatically when you attempt to present your view controller and the view is nil. (This can happen multiple times while your app is running if your view controller has to unload its view in response to memory pressure, another behavior you get for 'free' and that you don't want to call yourself)
If these methods are not being called in your view controller, then there must be something wrong with how you are presenting this view controller, which is preventing the framework code in UIViewController from calling these methods. Post that code and someone can help you figure out that bug.
Before you attempt to fix that bug, though, you should remove these from your code:
[self loadView]
[self viewDidLoad]
And then in your own implementation of viewDidLoad, you will want to call the superclass' implementation with [super viewDidLoad]
Remember that in loadView your only responsibility to set self.view to some valid view. That's it. UIKit code will then see that and call viewDidLoad for you.
Hope that helps.
The answer by Firoze Lafeer is correct, I just want to show how to force load the view property the correct way:
(void)self.view;
You should not do this! If you must, you are doing something wrong, but do not call -loadView and -viewDidLoad yourself at any circumstances.
When my view controller is first presented, I want it to potentially update a cache that provides the data for that view. However, when the user taps the back button from a deeper view controller to return to this view controller, I don't want to update the cache again.
Which event should I be using?
in init, I don't have all the parameters I need yet.
viewWillAppear will be fired every time the view will appear.
viewDidLoad will be fired every time the view has been loaded from the nib, which I believe could happen a second time if there's a memory warning. (Or is this wrong?) Since this is not a memory resident cache, it seems the wrong place to handle this.
having the caller call something extra is inelegant, if there's a built-in way to handle this.
To clarify, this is not a memory resident cache. This is parsing an XML file to binary. The binary is loaded and unloaded in viewDidLoad and viewDidUnload. This is a prerequisite for that step, making sure the binary is up-to-date prior to it being loaded.
Using init may work, but I would recommend a simple subclass of UINavigationController. Create a new method called setRootTableViewController:(UITableViewController *)controller, or something like it. In the method implementation call this:
[controller.tableView reloadData];
[self pushViewController:controller animated:NO];
reloadData will call all of your delegate and data source methods, and use them to update the table. If you want a special method call on your table view controller instead, you could change the method declaration to setRootTableViewController:(CustomTableViewController *)controller (or whatever your custom table controller is called), and replace the reloadData line with one that calls that method.
Then, in your app delegate, instead of creating a UINavigationController and adding your custom view controller, create one of these, and call this method to add the first view.
However, if you are using a nib to set the rootViewController, you can just override initWithRootViewController:(UIViewController *)controller, as I imagine that is what the nib will call to set the first view in the stack:
- (id)initWithRootViewController:(UIViewController *)rootViewController {
if ((self = [super init])) {
[(CustomController *)rootViewController doSomethingSpecial];
[self pushViewController:rootViewController];
}
}
Hope this helps!
I face a strange situation. In my controller, viewDidLoad is calling before init. Is there any technical reason behind that?
The viewDidLoad method is being called when accessing self.view inside the init method (since self.view should not yet be loaded from the nib the process seems to be fasten so it doesn't return nil).
I know this is a bit old post, but I'll post my point of view anywhere because I think it could help somebody.
Well, I've been in this same situation. I thought that viewDidLoad was being called before init method in my view controller class. But what was really happening was not that: the flow starts on init method, but jumps to viewDidLoad when calling [super init*], so my log messages in viewDidLoad method were being displayed first that those in my custom initialization.
I think that's it. I hope this to save some time to someone.
[Sorry for my English]
NOTE for UITabBarController:
I don't know what kind of UIViewController caused this for you but I faced a similar case with UITabBarController.
I thought it might help another one facing it with UITabBarController.
As far as I know all viewControllers call init before viewDidLoad, except for the UITabBarController and its subclasses.
As Andrew claims here, UITabBarControllers call loadView inside [super init] method, which causes the call to viewDidLoad. So the viewDidLoad method will be called before init has finished its job.
If you have some thing to setup in viewDidLoad you should perhaps do it inside init method after the call to [super init].
When you initialise a UIViewController from code, you use -initWithNibName:bundle:, whereas when it is initialised from a XIB, the XIB loading code will call -initWithCoder:. One, and only one of these two methods will be called, and they will definitely be called before -viewDidLoad.
There's no conceivable way that -viewDidLoad could be called first, unless you are calling it yourself (which you should never really do).
No, the viewDidLoad message is always called after init.
Are you sure init is called at all? There are several init methods especially for UIViewController, maybe another one is called instead making you think differently.
If you need more information, please paste the code of viewDidLoad and all of your init methods, and tell us how it is loaded (i.e. with code) or from a nib.
If your ViewController is being loaded from your main nib file, then most likely it is initWithCoder that is being called to initialize the controller.