Please let me know at what times init and loadView method gets called.
To my knowledge init method gets called only once when view is initialized and loadView is called anytime view is loaded. So, even if you are pushing a new view in the view stack and then popping it then also the loadView of the poped up view should get called. But when I am running my code in debugging mode, both of these methods are getting called once, irrespective of how many times I am loading the same screen. Please let me know if I am missing something.
you are right at some points :)
The init method is being called when the ViewController object is instantiated. The loadView method gets called every time a ViewController should load its view into memory. This can happen before the view is displayed for the first time OR when it should be displayed for a second, third,... time but had been removed from memory before. (this might happen if your app runs out of memory.)
If you want to execute some code every time the view becomes visible, you should have a look at the methods viewWillAppear/viewWillDisappear/viewDidAppear/viewDidDisappear.
loadView is called when you access the view property of your view controller and it's nil.
If the view has been unloaded (viewDidUnload has been called for memory purpose) then loadView will be called again. If not it will not be called.
What you want is viewWillAppear: or viewDidAppear:.
Related
Why does viewDidLoad function only called once in UITabBarController's root viewController?
I know viewWillAppear/viewDidAppear can called many times, but why doesn't viewDidLoad called many times?
It's because the tab bar controller gets loaded only one time. That's the process of initialisation and once it's initialised you don't need to do it again. Otherwise it would result in your memory getting loaded with more and more things until you run out of memory.
Is there a BOOL or some other way of knowing if viewDidLoad: has been called?
I need to change a view property every time my view has entered active, but if the view hasn't ever been loaded, I don't want to prematurely trigger viewDidLoad:. If there isn't way of easily telling if viewDidLoad: has been called, I'll simply add a BOOL called loaded set to NO in the view controller init, then change the view properties after entered active if loaded is YES or in viewWillAppear: if loaded is NO then set loaded to YES.
Use isViewLoaded. Other than that it does exactly what you want, there's not that much to say about it. The documentation is as simple as:
Calling this method reports whether the view is loaded. Unlike the
view property, it does not attempt to load the view if it is not
already in memory.
Perhaps you should init your UIView in viewDidLoad, and then change it in whichever way you need to inside viewWillLayoutSubviews.
Here's the pedantic answer to this question. If you want to know when viewDidLoad has been triggered, you have to implement viewDidLoad in your view controller
- (void)viewDidLoad
{
[super viewDidLoad];
viewDidLoadCalled = YES; // Not actually the best way to do this...
// Set up more view properties
}
But as Tommy says, you actually need to use isViewLoaded. This gets around the problem of doing a check like
if (!self.view) {
// do something
}
which inadvertently loads the view by virtue of asking about it.
Be aware that by the time viewWillAppear: is called, the view will always have loaded. Also, on older (pre-iOS 6 I think) releases, the view can unload and be reloaded many times over a view controller's lifetime. Refer to the very nice Big Nerd Ranch view lifecycle diagram for the old behavior. It's almost the same in iOS 6+, except that the view doesn't unload under low memory conditions and viewDidUnload doesn't get called:
If a new iOS project is created with an Empty App template in Xcode 4.3.2, and in AppDelegate.m:
self.window.rootViewController = [[FooViewController alloc] init];
and in FooViewController's viewDidLoad, the following:
NSLog(#"self.view is %p", self.view);
NSLog(#"self.view is %#", self.view);
will print out the view, so it looks like the default loadView will instantiate a view and assign it to self.view.
So if I override loadView with an all empty method, and comment out the second NSLog statement above, I expect the first NSLog statement to print out 0x0, but instead the app crashed due to bad memory access right at that NSLog line. Why would that be?
Okay, after a knee-jerk and obviously wrong answer, I tried this. The Empty App template would not have a rootViewController, so I used a single screen template. After running, I see that you are getting a stack overflow. In trying to access self.view, you are calling the view property on the superclass, which is then trying to load the view in order to return it, which is calling viewDidLoad, etc., as far as I can see. The other NSLog statement does the same.
The documentation for the view property in UIViewController states:
Because accessing this property can cause the view to be loaded automatically, you can use the isViewLoaded method to determine if the view is currently in memory.
It also has a link to The View Controller Life Cycle, which states:
The steps that occur during the load cycle are as follows:
The load cycle is triggered when the view controller's view property is accessed and the view is not currently in memory.
The view controller calls its loadView method. The default implementation of the loadView method does one of two things:
If the view controller is associated with a storyboard, it loads the views from the storyboard.
If the view controller is not associated with a storyboard, an empty UIView object is created and assigned to the view property.
The view controller calls its viewDidLoad method to allow your subclass to perform any additional load-time tasks.
So when you say:
So if I override loadView with an all empty method
You're deliberately breaking the life cycle, because when your overridden version of loadView finishes, it should have loaded a view. Because it didn't, you get a crash.
This is driving me crazy.
I am under the impression that awakeFromNib Method is called only once(even when that view is visited again), correct me if i am wrong.
I have an app with 3 views.
The last one being the subclass of a UIview where i draw using drawRect.
I had a working code with the method awakeFromNib in the last view, with the method being called only once how many ever times i visit the view.
Now i deploy the app to my device and update my Xcode to version 4.
When i run the code again and debug, the method awakeFromNib is called everytime the view is visited.
I dont think update would do such a crazy thing, but i am thoroughly confused.
Is there some kind of a memory leak or am i missing something?
Thank you
I am under the impression that awakeFromNib Method is called only once(even when that view is visited again), correct me if i am wrong.
-awakeFromNib will be called on each instance of a class whenever an instance of that class is loaded from a nib file. You should be able to expect it to only be called once on a particular instance but should handle it being called many times on different instances of any given class.
UIViewControllers will unload their views when they receive a memory warning and their view is not visible. The view will be reloaded the next time the view controller's 'view' property is called. You should understand and support this behavior to minimize your app's memory use as it allows you to only keep the currently visible views in memory at any given time.
It sounds like you are not expecting that controller's view to be unloaded and reloaded from your nib.
Interview question: For example, I have 3 classes A,B & C. I navigate from A -> B -> C, while pushing viewDidLoad function calls automatically and during popping viewWillAppear get called. But would it be possible to call my viewDidLoad function while popping?
No, Its not possible.Only viewDidAppear and viewWillAppear will be called by itself.You can call it manually.
All the best.
The viewDidLoad method is called when the view just loaded. Then viewWillAppear is called by the navigation controller when it's about to display the view.
If you want some code to be executed when the view is about to be displayed, be it when it's being pushed or when the top one is being popped, it makes more sense to use the appropriate method viewWillAppear for that, instead of trying to call the viewDidLoad method at a time when it isn't appropriate.
Can't you just move whatever code you have in viewDidLoad to viewWillAppear?
If the question is just "how can we call viewDidLoad while popping?", then it's simple:
- (void) viewWillDisappear:(BOOL)animated
{
[self viewDidLoad];
}
Just as a side note, if you have a view controller stack like A->B, it's possible that viewDidLoad will be called automatically on A when popping B if, while B was on top, the navigation controller unloaded A's view (if the app received a memory warning, for example)
You should not call viewDidLoad manually, it's not designed to be handled this way. Use viewWillAppear, as other users already mentioned.
As for the question whether it may happen that viewDidLoad will be called upon popping from a higher view controller: I imagine that may happen when the device got a low memory warning and unloaded the view controllers further down in the navigation hierarchy. Then the OS has to reload the view, I haven't however tested this and it's possible that this will never happen. The OS only unloads views when they don't have a superview, I didn't check whether upon pushing, views down the hierarchy actually get removed from the hierarchy.
ViewDidLoad should be for view creation.
ViewWillAppear - for Data Interaction if Server Request is Asynchronous. like calling API or any functionality.or any functionality we like to call when view going to appear.
ViewDidAppear - for Data Interaction if Server Request is synchronous.