Very basic problem with Tab Bar Application and Interface Builder - iphone

OK, here is how to re-create the problem had:
Create a new project, using the
Tab Bar Application
Add a UILabel within SecondView.xib
Add IBOutlet UILabel* myLabel; to FirstViewController.h
Connect up myLabel in IB.
Build and run.
When I click the second tab the app crashes with:
__TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION__

In IB, when using UITabBarController which has multiple UIViewControllers which all separately have their own NIB file assigned, there are two places where you need to set your UIViewController class file.
First, the obvious one was within the NIB file for each UIViewController.
Secondly, where I'd missed it, is withing the NIB file for your UITabBarController. In each UIViewController, not only do you set your NIB file, but also the Class.

Related

run time change of custom class in xib

I would like to change at run time the custom class as defined in a xib identity inspector.
This is to avoid to define lots of xib and view controller.
I've not found anything related to that subject.
Define a MyCustomUIViewController that would inherit from UIViewController. And set that controller as the file's owner of your XIB. This controller will have to support the defualt XIB IBOutlet and IBActions.
Then make other controllers (MyCustomUIViewControllerForThis, MyCustomUIViewControllerForThat, ...) inherit from MyCustomUIViewController
Then when you load it, that should give something like :
MyCustomUIViewControllerForThat* controller = [[MyCustomUIViewControllerForThat] alloc] initWithNibName:#"YourNib" bundle:nil];
This way, you can define in your XIB wanted Outlets and actions, and customize them if wanted in your MyCustomUIViewControllerForThat class.

How would I change the default loaded view controller in Xcode?

How would I do this? Is it simple enough to explain clearly? If not, a tutorial would be nice. I checked the web and this site and still couldn't find exactly what I was looking for.
The default XIB file loaded when your application start is MainWindow.xib by default.
If you want you app to load another XIB instead, this can be changed in the Info.plist file of your project.
In this XIB loaded when the app is launched (MainWindow.XIB by default), you will find:
a placeholder for the File's Owner (like in any XIB) which in the case of the XIB loaded by the application on startup is the UIApplication itself.
a UIWindow (the main and unique window of your iPhone app),
an object that acts as the delegate of your UIApplication (commonly called "the AppDelegate")
And probably a UIViewController too.
When the XIB is loaded at startup, the AppDelegate objet is instanciated (like all objects in the XIB except the File's Owner) and as it is set as the delegate of the application, application:didFinishLaunhcingWithOptions: will be executed. This code then generally add the viewController's view as a subview of your app window using a line like [self.window addSubview:self.viewController.view]. (As your AppDelegate have an IBOutlet that points to the ViewController in the XIB)
If you need to change the class of the ViewController used in your MainWindow.xib, change the class of the UIViewController in Interface Builder, and also change the type of the associated IBOutlet in the AppDelegate header file.

Why must I hookup an IBOutlet twice in a custom NIB / XIB?

I have created a custom myViewController class and it has the default view, as well as an IBOutlet (topleftView) to a subview.
I have created a custom NIB/XIB file to load this myViewController.xib. Inside the XIB file I have set the file owner to myViewController and set the UIViewController identity class to myViewController as well.
My question is why do I have to hook up and draw a reference from the IBOutlet in the subview to both the file owner AND the UIViewController in interface builder?
Just trying to get my head around it since this is the first time I'm creating a custom NIB/XIB. I usually just did everything in MainWindow but my application is getting too large so I want to spread things out.
If I don't have these multiple connections for the one IBOutlet to both the UIViewController and File Owner in the same nib file I crash with _EXC_BAD_ACCESS_ errors.
"Inside the XIB file I have set the
file owner to myViewController ..."
I'm not sure what you are doing here. I think this is where the problem is. How many items do you have on the top level of your XIB? It should just be File's Owner, First Responser and a View. If there is another controller object in here, that's your problem. Get rid of it.
"...and set
the UIViewController identity class to
myViewController as well"
This part is correct. To connect your custom UIViewController to the XIB, clock on your "File's Owner", go to the "Identity Inspector" then look under "Class Identity" at the "Class" field. Set this to 'myViewController' (or whatever you named it).
At this point you should do 1 ctrl-drag from your File's Owner for each outlet you have setup.
My question is why do I have to hook
up and draw a reference from the
IBOutlet in the subview to both the
file owner AND the UIViewController in
interface builder?
Short answer: You don't. Just set your File's Owner's class to the class name of your UIViewController subclass and you're set.

How to get a reference to a control in the view?

If I have a UIScrollView set up in the view via the Interface Builder, how do I get a reference to it in the ViewController implementation? I want to programmatically add labels to the scroll view.
For example, in C# if you have a textbox declared in the UI/form, you can access it by simply using the ID declared for that textbox. It doesn't seem this simple in objective c.
Thanks
Kevin
Assuming I understand you rightly and you are instantiating a view controller from a .xib containing subviews including the UIScrollView you want, there are two ways - first, you can find it in the subviews array that is owned by the view controller. Second, you can add an IBOutlet reference to it in your header file, then in interface builder make the connection using the connections inspector. Then you can refer to the object in your code, change frame, add labels etc.
You need to wire your ViewController up to your Nib files. This is pretty straightforward. This is your basic workflow for using Interface Builder on the iPhone/iPad:
Set the Class of the 'File's Owner' to the class of your view controller. You can do this by selecting the 'File's Owner' object in your nib window, pressing Command-4, and setting the class via the drop-down.
Create properties in your View Controller with the following format:
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
The IBOutlet keyword is a macro that evaluates to nothing. So it doesn't actually do anything to your code, it just exists to let Interface Builder know that the 'scrollView' property can be bound to.
Control-drag from the object you'd like to bind to your ViewController. In the popup you can select the property you'd like to bind to the scroll view object.
This sort of stuff is pretty basic Xcode stuff. If you read any tutorial out there it'll cover this. Good luck, and enjoy!
edit:
I should add that if you used the default "New UIViewController Subclass" from the New File dialog, it will have done step one for you. You'll have a nib file and a View Controller that already know about each other.
Yes, the code you wrote is all you need in your header. Just make sure you connect the Scroll View object to the property in Interface Builder.
Yup! 'viewDidLoad' is added after all the connections specified in the Nib file have been made, so you can be confident that scrollView point to the correct object (assuming everything in the Nib is wired correctly, which is an easy mistake to make)

What are the conventions for declaring a UITabBarController in mainwindow.xib?

I have a mainwindow.xib file with a UITabBarController as the base view controller of the app. So inside the UITabBarController I've added about 10 sub UIViewController objects as tabs. Most of them are just a UITableViewController subclass or a UINavigationController containing a UITableViewController subclass.
In this design, each UIViewController is fully loaded on app startup, including calling the viewDidLoad method of each view controller. Is there any way to get around that? Since the view controllers are just UITableViewControllers with no other outlets, it seems excessive to create a NIB for each tab (which I assume would allow the viewDidLoad to only get called when the user first switches to the tab? Or am I wrong on that?)
Anyway, my question mainly, is: how is it conventionally done? If you have 10 different view controllers on one UITabBarController, do you put them all in mainwindow.xib? If so, should each have its own NIB, and if not, where do you put them, and how do you add them to the tab bar?
What you want to do is to define the UIViewController views in a different xib file for each view - the reason they all get instantiated is that when the xib loads, all objects held in the xib load - and that means all your views and view controllers since you have defined them there.
In MainWindow.xib where you have the tab bar defined, you can still set within each tab the view controller type that will be called and also the XIB file to use for that type (create a new project with the "TabBar application" template and the second default view will be like this).
Then as you press tabs the view controllers will be instantiated from the different XIB files you have defined.
Note that this means if you are using IB to add buttons to the navigation bar, you have to do that back in the TabBar xib and not in the xib you use to define the view. You can still link actions to the view controller definition within the tab.
The way Apple suggests doing it for pure NIB files is how you say: Each sub-view in its own NIB file.
Instead of doing this, I would create the UITabBarController programmatically. That way you can define all your simple views in code, and still load complex views from NIB files.
Personally, I prefer creating as many of my views programatically as possible. The compiled code has a smaller footprint than the NIB files and I feel like I have more control. I mostly use Interface Builder to mock up applications.