viewDidLoad is called twice - ios

My viewDidLoad in a view controller is called twice. Once by [UIViewController View] and a second time by [UINib instanciateWithOwner:Options]. Why is this happening? Can it be prevented?

Any code you put inside of viewDidLoad should be able to run multiple times with out any issues. If you have code that only needs to run once for your controller use -awakeFromNib. The reason is because the view of the view controller can be unloaded and loaded multiple times. The code inside of viewDidLoad should only modify the UI to reflect the current state.
Now that I got that out of the way, your particular issue looks to be a bug. See Ned's answer.

Is this the same problem?
Why is viewDidLoad called twice when the rootViewController property of UIWindow is set?
Looks like it might be a bug in XCode 4.

You might have to check the object building mechanism. If there is only one nib file with reference to the controller, then this method should not be called multiple times. (unless if the object is getting rebuilt).
I think you might have to make your code within ViewDidLoad idemPotent. It is always better to make sure, that framework call back methods make this assumption.

There are two possibilities, whereby this issue happened in my iOS device frequently.
Rule #1: Do not call any view related setup in [init] function, all view related setup must be done in viewDidLoad and viewWillAppear.
Rule #2: Check viewDidLoad and viewWillAppear, are they calling correct super function? For example viewDidLoad -> super viewDidLoad and so on.
Hope this helps.

In my case, I used self.view (once) in viewDidLoad while calling viewDidLoad in my unit tests. This resulted in two calls. However, when I replaced [testedViewController viewDidLoad] with [testedViewController view], the double call problem was gone.

Debugging this showed that viewDidLoad was called a second time by #IBInspectable. The root controller is a UITabbarController. #IBInspectable was setting the tab in the storyboard. Not sure if this is a UIKit bug but try checking for this. You should never need to check viewDidLoad for double calls if your project is setup correctly.

Related

viewWillAppear not called on contained VC if parent is between viewWillAppear and viewDidAppear

I recently ran into a headache with iOS view controller containment.
Everything works as expected, except in one particular case:
If a child view controller is contained by a parent while the parent is in-between its viewWillAppear and viewDidAppear calls, then viewWillAppear will never be sent to the child VC (the child VC sees viewDidLoad and then viewDidAppear).
This edge case can come up e.g. if you create and contain a child VC as the result of an asynchronous network call, which might land in-between the parent's viewWillAppear and viewDidAppear.
I've put together a demo here: https://gist.github.com/cellularmitosis/8205610a80112eebd96c
To reproduce this locally, create a new "Single View Application" iOS project in Xcode, then replace the contents of ViewController.swift with the above gist.
Am I missing something obvious here, or is this a bug on Apple's part?
I'm guessing this means I need to override shouldAutomaticallyForwardAppearanceMethods() to return false, and then manually call beginAppearanceTransition and endAppearanceTransition?
Follow-up: This is the workaround which I'm currently using: https://gist.github.com/cellularmitosis/56d734ab087a3f283455
I implemented a view controller transition state tracker, and if the parent VC is in-between viewWillAppear and viewDidAppear, then we answer false to shouldAutomaticallyForwardAppearanceMethods.
containChildViewController is updated to handle both true and false for shouldAutomaticallyForwardAppearanceMethods.
Definitely a kludge, but it appears to work.
TODO: There may be an analogous bug during the viewWillDisappear -> viewDidDisappear transition. I haven't checked.
EDIT: edited the workaround gist to use beginAppearanceTransition/endAppearanceTransition instead of directly calling viewWillAppear/viewDidAppear

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.

Why is viewWillAppear: on my contained view controller being called twice from transitionFromViewController?

I have a MainViewController managing a container view. In MainViewController's viewWillAppear: method, I check if the user is a member or a guest, and then call transitionFromViewController:toViewController:duration:options:animations:completion: to swap the MemberViewController or GuestViewController into the container view as appropriate.
The problem is, viewWillAppear: on my MemberViewController is being called twice from within transitionFromViewController.... This is a problem as I'm making network calls there which I don't want to duplicate.
Why, and how do I fix it?
I have no idea why, but moving the code that calls transitionFromViewController... from MainViewController's viewWillAppear: to its viewDidAppear: fixed the problem.
(If you can clarify any of this for me, it would be most helpful.)

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

viewDidLoad calling before init?

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.

Resources