The documentation says "This method is called after the view controller has loaded its associated views into memory." My questions are:
1) if I initialize a view controller is viewDidLoad called, or does the view actually have to be added as a superview of the current view for it to be called
2) If I add the view controllers view, and viewDidLoad is called, then I remove the view controllers view, then re-add it again later, will viewDidLoad be called again?
viewDidLoad is called whenever the view is loaded. This happens when the vc.view property is accessed. It typically happens right before adding the view to a view hierarchy, but it can happen earlier if the property is accessed earlier.
If you remove your view from a view hierarchy, and then a memory warning happens and viewDidUnload is called, then viewDidLoad will be called again if the view property is accessed again. But this is the only way for it to be called a second time; if your view never unloads, then viewDidLoad will never be repeated.
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.
I'm trying to hide a button inside the controller's init method. But its not working. I have created a outlet to the image to access it from the controller. Dont know why the image is not get hidden.But I can do it inside the viewdidload method. Is it because the view still not loaded?
Yes the view will be loaded when viewdidload is called. so any changes to the existing UI should be called in view did load or view will appear
The UI components you're talking about are all subviews of the UIView that is owned by the UIViewController that you're creating. But that UIView and its subviews aren't actually created at the time the init method is called, so you can't change them there.
viewDidLoad is the appropriate place for any code that only needs to run once after the view has been created, but before it has been displayed.
viewWillAppear: and viewDidAppear: will run every time the view is about to or just did become visible again respectively.
Actually, the whole view hierarchy will be loaded when the loadView message is sent to the controller. This message is also sent automatically the first time you access the controller's view. That's why your message to the button gets discarded in the init method, because the object is not yet 'unfrozen' from the xib/storyboard. And yes, the appropriate place to do your view customization would be inside the viewDidLoad delegate method.
PS. Also... if you want the button to be hidden upon initialization, why don't you just check its hidden property in your xib/storyboard ?
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.
I have a main view controller with two other view controllers as member variables, representing different "tabs" within the main view. They are hidden and shown when the "tabs" are tapped. All of these are in a nib file.
When I get a memory warning, viewdidunload is called and the view is dumped, as expected. However, they are never recreated and viewdidunload is never called again. The main view controller is attached to a nav controller.
What gives?
When a memory warning occurs, the system may unloads views of view controller that are not visible on screen at that time. The action which triggers the reloading of those view controller's views is something actually trying to access those views. In other words, when some code is executed which accesses viewController.view, if that view had been unloaded, it is reloaded.
In practical terms, this means that your unloaded views will usually be automatically reloaded at the point where they would be shown again. So after a memory warning, change the current view using your tab controller variant, and you should see that it is reloaded (if it was indeed unloaded).
I solved this by dumping and reloading the associated view controllers on a memory warning, but the original cause remains a mystery.
Viewdidappear is never called on the views either, I suspect this is related to whatever the explanation is.
If you're doing view controller containment you have to manage the appearance and disappearance of your view controllers at all time.
When a view controller unloads its the view whole hierarchy of that view is destroyed and is, of course, not saved. When the view of that parent is reloaded, the -loadView and -viewDidLoad code are executed as well as the appearance methods. These methods will not do more than what you tell them to do. If you do not rebuild the view hierarchy of your children in viewDidLoad don't expect them to reappear magically.
The job of the container controller is to load the view of its children (the ones that are visible) and layout them in its own view.