Some times when I push ViewController into Navigation Controller,
the viewDidLoad() method of the View Controller is not called.
And this cause my application to crash. I would appreciate any help.
I forget to mention that I load the view from the nib before I push it to the Navigation Controller.
Thanks,
Sarah
The viewDidLoad method is only called when the view is first loaded from the Nib file. If the view was already loaded and you push the view again it will not fire again.
Depending on what you want to do, you may want to use viewWillAppear or viewDidAppear instead.
Once the View is loaded and added to the Controller's stack, you will not see this called again. You would need the view to get popped off the stack and pushed again to see it. You can always be assured viewWillAppear will get invoked everytime you return to the view. This allows you to do any housekeeping that may be in order (which i assume is the goal).
Related
I know there are a lot of similar questions, but I can't find one that specifically addresses this.
Why is self.navigationController null when called in viewDidLoad, but correct when called from outside viewDidLoad?
This is my output from NSLog(#"%#",self.navigationController); The first is called in viewDidLoad, the second I add a button to the interface with a method that calls NSLog(#"%#",self.navigationController);:
NavApp[31524:11003] (null)
NavApp[31524:11003] <UINavigationController: 0x6e21190>
I know there must be some simple explanation for this behavior, I'm just curious as to what it is. Thanks!
A view controller's view is loaded when you first access the -view method/property on that controller. After the view has been loaded, the viewDidLoad method is called. This is pretty straight forward. You also have to remember that the view can be loaded/unloaded multiple times if memory warnings are received while the view is off-screen.
So viewDidLoad does not mean your view controller has been inserted into a navigation controller. In the process of pushing a view controller onto a navigation controller, its view will be accessed and loaded, but this will happen before the whole push is complete. So viewDidLoad is clearly getting called before the navigationController property has been updated.
You also have to consider that some other part of your code could be accessing the view controller's view before you even push the view controller onto the navigation controller.
So viewDidLoad is the wrong place to do what you are doing. You probably want to use a method like viewDidAppear: so you know that the view controller's view is part of the view hierarchy when it is invoked.
For a controller that is pushed by a nav controller, if in its desired init method you create the details of the pushed controller's view, my understanding is that if the view is removed later because it is offscreen (due to low memory, for example), you may never see it again since the init is only called once when loading the view controller, and subsequent pushes may show nothing.
It would seem a better place for this view setup is in the viewDidLoad or viewWillAppear, that way it will be recreated correctly if the controller needs to build the view the next time it is pushed.
Yet I see tutorials that often put the pushed view controller's view setup in its init method; how important is this?
View setup should be done in viewDidLoad (or in loadView if you're not using a nib), for exactly the reason you're describing. If you need to know the controller's top-level view dimensions to set up the subviews, then do it in viewWillAppear:.
Usually, a view controller that is popped is immediately deallocated; if the app needs to show the same view later, it creates a new view controller for it. So in that scenario, the app won't show “nothing”.
If the app pushes or presents another view controller on top of the first view controller, the first view controller's view can be unloaded. If the app never pushes or presents a second view controller over the first view controller, the first view controller's view can't be unloaded until the controller is popped, at which time (in most apps) the controller is deallocated anyway. So in that case, setting up the view in init won't cause trouble.
But it's still bad design to set up views in init. You might change your app later to push or present a second view controller, thus creating unexpected unsafe behavior.
Also, it's common to create a view controller, set properties on it, and then push it. If the properties affect the controller's view hierarchy, then init is too early to set up the views.
By my count, the only two instances when viewWillAppear is called is when you initialize your view controller, or when you pop off the view controller that's on top of it on the navigation stack (ie pushing the back button on the viewcontroller ahead of it). Are there any other instances when viewWillAppar is called? I don't believe it's called when the app becomes active. Interested to hear some responses on this.
viewwillappear method is called as and when the view controller's view is added to the window. ( if the view is already in the window and is hidden by another view, this method is called when the view is once again revealed). The method is a notification to the view controller that the view is about to become visible. You can override this method to make any customizations with presenting the view.
This will also be called anytime addSubView is called, with your view.
Here's the scenario, switchViewController is the view added to the main window. So switchViewController is the main view, so if I want to go view B, I will addsubview of view B, there isn't a need to remove switchViewController's view right?
The issue is after I go back from view B to switchViewController's view, the method viewWillAppear is not being called anymore.
Why is it so?
viewWillAppear: is not called automatically when a view is removed from or added to the view hierarchy. It is the responsibility of the view controller to call it at the right time. The built-in view controller classes do this whenever you present or push a new view controller. Since you do not use this mechanism in your app, the method doesn't get called (unless you call it yourself).
That's because it never disappeared, you were just putting something else in front of it. If you want to navigate from one screen to another and back, they should be separate view controllers, and you should be using UINavigationController and its pushViewController:isAnimated: method.
It's not getting called beause your just modifying the first view, not navigating to a different one.
You might consider embedding your view in a Navigation controller, then calling your ViewB with
[navigationController pushViewController:viewB animated:YES];
Using a navigation based view hierarchy. I have a root view controller, and multiple view controllers that branch out from the same when a button is pressed. When a user presses the back button on the UINavigationBar, the current viewcontroller is popped and the display animates back to the rootviewcontroller.
The problem is, I want the viewcontrollers to UNLOAD whenever they are popped. Seems like they are not unloading because when I go back to them they are still in the state they were when they were popped.
How do I unload the viewcontrollers after navigating back to the rootviewcontroller?
-viewUnload is called after the application receives low-memory notification.
It is the default implementation of -didReceiveMemoryWarning that calls -viewUnload.
What you probably want to do is put what you want to do into -viewDidDisappear based on what you've described.
When you pop a viewcontroller from a navigationcontroller, there should be no more references to the viewcontroller left and the viewcontroller should be deallocated at that time. This should give you the results you expected. You can test if the viewcontroller is being deallocated by adding a break point in -dealloc method.
If dealloc does not get called, check if there is a retain cycle. Specifically check if a child object is retaining the viewcontroller.
in -viewDidDisappear why not [self release]; just need to make sure you have a lazy loader so it will load back when needed.