Making sure that objects in nibs are initialized before applicationDidFinishLaunching - objective-c

and thanks for taking a look at my problem.
i have two view controllers in my app and each has its own nib file. an instance of controller1 is stored in MainWindow.xib and an instance of controller 2 is in Controller1.xib.
is there a way to make sure that controller 2 is initialized before the app delegate is sent applicationDiDFinishLaunching?
the actual setup is much more complicated with many other view controllers, so i really don't want to put everything into MainWindow.xib. plus doing so will reduce reusability.
thanks again!

is there a way to make sure that controller 2 is initialized before the app delegate is sent applicationDiDFinishLaunching?
No. Well, maybe yes, but it's not how view controllers are supposed to work. The view controller is there to defer the loading of the nib, which is a rather expensive operation, until the view is really, really necessary. So, if you need controller2 right when applicationDidFinishLaunching is called, you shouldn't put it inside the nib which is controlled by another view controller.
If I were you, I would stop instantiating the view controllers in the nib file at all, and just create them inside applicationDidFinishLaunching:, as in
-(void)applicationDidFinishLaunching:(UIApplication *)application
{
....
self.controller2=[[Controller2 alloc] init... ];
....
}

I believe applicationDidFinishLaunching is the absolute entry point in which you have control of the code. That's conceivably the earliest place to load anything.

Related

Does dismissViewControllerAnimated remove the controller's instance

For my app, I want to have a few different instances of the same view controller. For now, I am just creating a new instance like this:
iSafeViewController *tab = [[iSafeViewController alloc] init];
[tab setModalPresentationStyle:UIModalPresentationFullScreen];
[tab setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentViewController:tab animated:YES completion:nil];
Great. And since this is done in the iSafeViewController class anyway, I have another button that currently just dismisses the latest controller on the stack.
- (IBAction)closeTab:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
Okay, however, I really want to be able to go back to these instances. So, I have two questions.
Does dismissViewControllerAnimated remove that controller's instance from memory. If not, is there a way I can re-present it.
There is probably a better way to navigate through viewController instances then presentViewControllerAnimated. At the very least, is there a better way to create new instances of one's viewController and be able to navigate to each of them, hopefully not in a stack. In other words, if there are three viewController instances, is there a way I can go from the third to the main one?
Any ideas would be appreciated. Thanks.
"Does dismissViewControllerAnimated remove that controller's instance from memory? If not, is there a way I can re-present it."
Calling dismissViewControllerAnimated does not explicitly remove a view controller from memory, but if no other part of the code is storing a strong reference to the view controller, once the presenting view controller dismisses your VC, it may be deallocated as per the normal memory management system.
So if you ensure something in your code has a reference to your view controller (aside from the VC which is presenting it modally), it won't disappear after being dismissed, and yes this means you can re-use it.
As for "random access" to view controllers: you could use UINavigationController and use methods like popToViewController:animated: and multiple calls to pushViewController:animated: (without animation!) to create the effect of travelling to arbitrary view controllers. This feels like a bit of a hack.
Alternatively, and preferably, you could write your own custom container view controller. This is a view controller that deals with presenting other view controllers. See Apple docs.
Here's a good WWDC video on the subject: Implementing UIViewController Containment
Further reading:
Container View Controller Examples
http://subjective-objective-c.blogspot.co.uk/2011/08/writing-high-quality-view-controller.html
Custom container view controller

Reuse UIViewController instances when using storyboard

I decided to give the use of storyboards a go in my current iPhone app. I am facing a bit of a problem. I really need to reuse my UIViewController instances.
What do I mean by that? Well, for example I have a table view controller. When I tap a cell, another view controller is loaded from the storyboard and pushed onto the navigation controller stack. This all works well, but it takes about half a second to a second each time this view controller is loaded. Before I was using story boards I simply solved this problem by caching the created instance so the second time you tap a cell the view controller can be immediately shown.
By caching the created instance I mean something like this:
if (!cachedInstance) {
cachedInstance = [MyViewController new];
}
[self.navigationController pushViewController:cachedInstance];
Does anyone know how to accomplish this using the storyboard? Thanks in advance.
If you are using segues, you will need to create custom segues in order to use a cached view controller like you did before. Otherwise, the typical "push" segue will create a new instance of the view controller for segue.destinationViewController. If you write a custom UIStoryboardSegue class and use custom segues you can override initWithIdentifier:source:destination: and put your cached view controller in for the destinationViewController, and then override perform to use the classic pushViewController call.
That is how you handle the segue if you are really intent on using them. I would just skip it though, unless you really want the fancy arrows to lay everything out on your storyboard. If you skip it you can just instantiate the view controllers into the cache and then push them on just like you did before.
If your question is more about locating a view controller inside a storyboard then you can use:
UIViewController *vc = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"Some View Controller"];
Then you can save that to your cache and push it like you did in your example code.
Hope that helps.

UIViewController within a UIViewController

So I have a viewControllerA, and I want to add another View managed by viewControllerB to it. There is only one UISlider activating a simple action in viewControllerB. It won't crash if I don't touch this UISlider, it will once I use UISlider. I am using ARC. I am using:
[self.view addSubView: viewControllerB.view];
to add viewControllerB to viewControllerA. Am I missing something? Thanks.
OK. It looks like a really simple situation. I just added one view controller and one action. Here is the demo project code on github: https://github.com/randomor/Demo
The reason why I want this to work is because I have another app that will create a view controller on the spot and add it to anther view. And I don't want to do it modally, because I don't want the new view controller to cover the whole screen. Thanks.
SOLUTION: So I'm now just using the latest ViewController containment API:
[self addChildViewController:viewControllerB];
It works! as long as I added this line, the event will be passed to its own controller and it stopped crashing.
i recommend you, to use the following code
in ViewControllerA.h
#import "ViewControllerB.h"
in ViewControllerA.m (where you want to push the new controller)
ViewControllerB *newController = [[ViewControllerB alloc]init];
[self presentModalViewController:newController animated:YES];
in ViewControllerB.m you will need
[self.presentingViewController dismissModalViewControllerAnimated:YES];
to make it vanish again.
concerning multiple controllers for one open screen (Apple ViewController Programming Guide):
Each custom view controller object you create is responsible for managing exactly
one screen’s worth of content. The one-to-one correspondence between a view controller
and a screen is a very important consideration in the design of your application.
You should not use multiple custom view controllers to manage different portions
of the same screen. Similarly, you should not use a single custom view controller
object to manage multiple screens worth of content.
You should try and avoid the practice of nesting UIViewControllers. While it is technically supported in iOS5, it is ill-advised, for many reasons, including the type of problem that you're having (you have a dangling pointer to a UIViewController, which is why you are crashing).
http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/
Although this question is extremely vague, I imagine that you are not keeping a reference to View Controller B, and so when view B tries to interact with it, it causes EXC_BAD_ACCESS.
What's the object that is set as the target for the slider? If it's a EXC_BAD_ADDRESS, then you may not be retaining the target, most probably the view controller for the slider.

What awakeFromNib really is?

I'm just a newbie of Objective C iPhone programming... I see a lot of codes that contains method named "awakeFromNib".. What does it really mean? how and when and where to call this method?
Thanks...
awakeFromNib is called for objects (views, controllers, etc.) that are being archived in xib/nib files. This basically means that xib/nib was unarchived, all connections (IBActions/IBOutlets) for all objects are made and you have a working object graph.
I use it when I have a custom view class that I gave to a certain view in my xib.
You don't call it. After nib file load, every view inside of it, be it button, UIView or something else, calls awakeFromNib from their respective class automatically. You override this method for setup and layout, as you would do with init method if you were creating a subview programmatically.

initWithNibName VS viewDidLoad

I've been trying to understand for hours when I should use the viewDidload: and when I should use initWithNibName: to set up the properties of my viewController.
For instance, I'm using a TableViewController and I'm setting all its properties (such as the backgroundColor, the separateColor, the toolbar items) in initWithNibName. It is the right way to do ?
If somebody could enlighten me.
Thanks
You should set up your properties in the viewDidLoad. This method is called by the system when the controller's view is loaded into memory. The initWithNibName: is something that you call when you create a controller instance from a nib file.
That is to say, that if you set up your properties in the initWithNibName: and instead you call init, your controller might not be in a good state; thus, it's best to do in viewDidLoad.
You should use viewDidLoad: method of your controller. To quote from Apple's documentation on initWithNib:
The nib file you specify is not loaded right away. It is loaded the first time the view controller’s view is accessed. If you want to perform additional initialization after the nib file is loaded, override the viewDidLoad method and perform your tasks there.
initWithNibName: is called when the NIB is loaded and instantiated.
viewDidLoad: is called when your view is actually presented onscreen.
And yes - I believe that in your case, setting colors and such are best done in initWithNibName