Is it possible to reload the main view of a view controller from its XIB file in its viewWillAppear method? The method make significant changes to the view, which would be difficult to reverse. When the view controller gets pushed off the navigation stack, the method has to deal with a changed view. It would be much easier if I could just relax the view from the XIB. It seems that the Apple implementation assumes that the view will remain static, and can therefore be reused.
You can update large portions of your view in the -viewWillAppear. There is no stipulation what you can or can't update there.
Just make sure you account for everything, when you do. I have used -viewWillAppear to refresh my view with information, so you can use that to load an initialize what you need rather than -viewDidLoad.
Related
Suppose I have 2 views. In the first view, I allocate memory to displaying many UI components such as an UILabel, UIImages, etc.
Suppose the user navigates to the next view (via UINavigationController)
Is it OK to deallocate memory assigned to displaying UI components in the first view and then initialize them again once the user goes back to the first view (in viewFirstLoad or the appropriate function)?
It seems to me if you don't do this, then memory will keep on increasing the longer the user uses your app in that particular session.
Is this not allowed? frowned upon? impossible?
It is perfectly normal and in fact, that functionality is built in standard UIViewController - when controller is not displayed its view may be released from memory and you can release all its subviews (e.g. retained through IBOutlet references) in controller's -viewDidUnload method.
When controller needs to display again it reloads its view again.
It depends. Generally, the rule of thumb is that you should free objects that you don't need. If your view is just a view, then yes, I'd release it and all of its subviews. If your view has data that was obtained through a lengthy retrieval process (e.g. a web service call), I'd probably hold onto the data somewhere so that I don't have to go back out and retrieve it when the user goes back to the first view.
To clarify a little: Apple recommends you display data specific to a view in it's -viewDidLoad method, such as setting text on labels. Then, in -viewDidUnload you should release (or nil outlets of) the view objects you setup in -viewDidLoad. It's critical you implement -viewDidLoad, as the base UIViewController code checks that it's subclass actually implements -viewDidLoad before it assumes it can unload the view (and therefore call -viewDidUnload). Failing to implement -viewDidLoad results in the controller thinking it can't recreate your view at a later time, and so it doesn't unload the view from memory. A developer I know experienced this same problem, took forever to track down.
Does presenting a ViewController cause the presented controller to run its viewDidLoad method?
If a view1 is loaded and another is presented. Then something triggers to present view1. Will it run through its viewDidLoad method?
IF not how should this be done? ViewDidAppear?
Building on what Jesse relayed, the viewDidLoad is called when the view is loaded into memory (usually the first time the view controller is ever about to be presented since app launch - simplified, but this will suffice for now).
When you display other view controllers and then come back some how to this original "view1" view controller, unless there was a memory event that jettisoned it from memory, it will NOT call viewDidLoad again.
Instead, it will call the following, in order:
viewWillAppear:
viewDidAppear:
In viewWillAppear:, you have a place to do things "off screen" before your view controller is displayed.
In viewDidAppear:, you can do additional operations that are appropriate for when the view controller's view is already visible. For example, you want to run some little animation that the user will see once the view controller is fully visible.
In both of these methods, make sure you call super's implementation before you do anything. Also, to learn about this lifecycle, set a breakpoint or NSLog() statement in each of these methods (viewDidLoad, viewWillAppear, viewDidAppear) to see when they are called.
There's a concept piece in the Apple docs on View Controllers that is worth the 20 minute read - it'll clear up a lot of this key life cycle information about View Controllers, and these are central to iOS development. See the section "Understanding the View Management LifeCycle" at:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/BasicViewControllers/BasicViewControllers.html%23//apple_ref/doc/uid/TP40007457-CH101-SW1
viewDidLoad is only called when the view is loaded into memory. Usually the first time it appears (could be more often if there are memory dumps and etc).
viewDidAppear: is called every time the viewController's view becomes the 'active' view in the window.
I'm trying to figure out what logic should go into the different UIViewController methods like viewDidLoad, viewDidAppear, viewWillAppear, ...
The structure of my app is that I have a root view controller that doesn't really have a view of its own, rather it has a tab view controller and loads other view controllers into it. But in the future, it may have to load other view controllers instead of the tab bar controller depending on app logic.
My main question is, what do people usually put into the viewDidLoad, .... methods.
Currently I:
viewDidLoad - setup the tab bar controller and set its view to the view controller's own view
viewDidAppear - check if user has stored login info
if not - present with login
if so, login and get app data for first tab
I'm trying to figure out now if my logic for setting up my tab bar controller should go into loadView rather than viewDidLoad.
Any help would be great. Small examples found on the web are great, but they don't go into detail on how larger apps should be structured.
You should not implement both -viewDidLoad and -loadView; they are for different purposes. If you load a NIB, you should implement -viewDidLoad to perform any functions that need to be done after loading the NIB. Wiring up the tabbar is appropriate there if you haven't already done it in the NIB.
-loadView should be implemented if you do not use a NIB, and should construct the view.
-viewWillAppear is called immediately before you come onscreen. This is a good place to set up notification observations, update your data based on model classes that have changed since you were last on screen, and otherwise get your act together before the user sees you. You should not perform any animations here. You're not on the screen; you can't animate. I see a lot of animation glitches due to this mistake. It kind of works, but it looks weird.
-viewDidAppear is called after you come onscreen. This is where you do any entry animations (sliding up a modal, for instance; not that you should do that very often, but I was just looking at some code that did).
-viewWillDisappear is called right before you go offscreen. This is where you can do any leaving animations (including unselecting tableview cells and the like).
-viewDidDisappar is called after you're offscreen (and the animations have finished). Tear down any observations here, free up memory if possible, go to sleep as best you can.
I touch on setting up and tearing down observations here. I go into that in more depth in View controllers and notifications.
viewDidLoad will be called once per lifetime of each UIViewController's view. You put stuff in there that needs to be set up and working before the user starts interacting with the view.
viewDidAppear is called whenever the view has appeared to the user. It could potentially be called more than once. An example would be the root screen of an app using a UINavigationController to push and pop a hierarchy of views. Put stuff in there that you'd want done every time. For example, you might want to hide the UINavigationBar of the root screen, but show it for all subscreens, so you'd do the hiding of the bar here.
Therefore, you'd put your logic for setting up your UITabBarController in viewDidLoad, since it only should be done once.
Regarding your app, is there a reason why you don't just make the UITabViewController be the controller loaded by your app delegate? It seems that you have a level of indirection in your app that you may or may not need. It's probably better to simplify it now, and refactor later if you need something more complex.
I'm starting to go a little crazy with this one.
I have an iphone application with a somewhat complex view structure it is a tabbed application with either a view controller or a navigation controller on each tab.
The main controller for one tab uses the viewDidAppear callback to reload any data that has been modified since the last time it was displayed and this behaves fine in most cases. The issue I have run into is that when I navigate in subviews(within the same tab) using the standard navigation controller push/pop mechanism the viewWillAppear and viewDidAppear on the main view always fire when I navigate back to it.
However if I load a modal view controller and then dismiss it, the viewWillAppear continues to fire but the viewDidAppear stops firing.
I am almost certain that this is tied to the need to manually call these callbacks on the modal controller but I cannot find the reference info on how to do this correctly. (I only see this viewDidAppear bug on the hardware, in the simulator it works as I'd expect)
If you need further clarification let me know and thanks for any input.
Yes, this is how it works. You are better off switching to using viewWillAppear.
This has the added advantage of updating the data before the screen is redrawn.
viewDidLoad should be used for one-time setup operations:
Setting titles
Instantiating subviews, content arrays
Anything else related to the infrastructure of the view in question
After that, use viewWillAppear: to do anything related to refreshing data. You shouldn't have to call any of the viewDid/viewWill methods manually – that's handled by the innards of UIView. viewDidLoad won't fire after you dismiss a modal view controller because, more than likely, the view never unloaded. viewDidLoad fires fine when you're navigating the a view controller stack since the new views need memory, requiring other views to unload, then be reloaded when they reappear.
At the beginning of my app, I am initializing a UIViewController with a NIB.
At some point, I am removing the view from the screen, then releasing it, so it no longer takes up memory. (At this point I am also saving the state of the view in the UIViewController)
At a later point, I want to recreate the view and bring it back on screen. I will then restore its state. (Using the same UIViewController, not a new one, since it saved the state)
My question, is when I recreate the view, how do I do so from the NIB, or is this not possible?
To me, the obvious remedies are:
Don't save state in the UIViewcontroller (Where does convention dictate that I do save state?)
Don't release the view (maybe just release all of its subviews?)
Don't load my view from a NIB, create programmatically (seems to go against using IB for everything)
Note:
I am not using a UINavigationController, I am handling the swapping of the views myself, since thee are only 2 of them.
If you need that level of control, you dont really want to use IB. Because, as you noted, if remove the view and then later recreate it, you would have to do it in code then anyway. Just design most of the views in IB, then write some code that generates just this view. Then you can call that same method again later to recreate that view when you need it.
You may be able to archive it and later turn it back into an object, but that seems like an inelgant solution. IB does not allow for dynamic creation of controls at runtime, even if they used to exist but don't anymore. There is no shame in leaving IB out of loop for this. In fact it's probably a good idea.
OR
If its a complicated view with a lot of pieces, put the view in it's own nib, and make a view controller for it. Then you can simply instatiate the view controller with the nib name, and add the controllers view as a subview to you main view. Then your view controller handles loading of the nib, and you get to design it in IB. Nothing says the view of a view controller has to take up the entire screen either.
self.otherController = [[OtherController alloc] initWithNibName:#"Other" bundle:nil];
[self.view addSubview:otherController.view];
Don't create the view controller and the view in the same nib file; either create the controller in code or put the view in a separate nib file. If you later nil out your controller's view property, the controller should recreate the view.
But why are you so worried about this? There's already a mechanism to automatically free up the view if you're low on memory: the low memory warning. It will only destroy and recreate the view if you actually need to do so, and it's built in to the system so you don't have to do anything. Remember the saying about premature optimization?