Let's say, I have three UIViewController
UserFormViewContoller
NewUserFormViewController : UserFormViewController
UpdateUserFormViewController : UserFormViewController
So, NewUserFormViewController and UpdateUserFormViewController view controller inherit from it's parent to share the basic functionality. The different will be their method, create and update.
The views also have a lot of things in common, almost everything. The different view components between NewUserFormViewController and UpdateUserFormViewController is a button to perform save task (create or update)
Is it possible to have two UIViewController sharing one XIB file? Let's say, UserFormViewController.xib and then I do
[[NewUserFormViewController alloc] initWithNibName#"UserFormViewController" bundle:nil];
[[UpdateUserFormViewController alloc] initWithNibName#"UserFormViewController" bundle:nil];
The other question but important is, when I edit xib file with Interface Builder, what owner's reference outlets and IBActions is it talking about, NewUserFormViewController or UpdateUserFormViewController?
(IBActions and Outlets showing when we right click at the Placeholders -> File's Owner)
If that's so, I will just use one XIB file and programmatically add other specific view component (It would be great to have only one XIB file so that I can make some changes at a place but effective on both)
The "file owner" is just a convention so that XCode can show you the correct IBOutlets and IBActions in its inspectors. If you create a generic (in OO terms: abstract) UserFormViewController (.h, .m, .xib), wire it in IB; then subclass it in two NewUserFormViewController and UpdateUserFormViewController, they'll inherit their outlets and actions from their parent class without any problem.
#Armaan I met the same problem by simply call subclass' alloc init method. I fixed this problem by using initWithNibName: and supply xib file name of parent class.
Related
I've read Apple's "Your first iOS application" guide and everything there seems crystal clear to me. However, when I try to understand how View-based iOS application template provided in XCode works, I run into some interesting conundrums.
I understand that the application gets the main nib file name (usually, the MainWindow.xib) form the *-Info.plist file. What I do not understand is, how does the XCode know which nib file is associated with the controller that is created with this View-based application template by default. In the guide, you start with the Window-based application, and you "have to write" something like:
MyViewController *aViewController = [[MyViewController alloc]
initWithNibName:#"MyViewController" bundle:[NSBundle mainBundle]];
[self setMyViewController:aViewController];
which makes perfect sense. However, it turns out that in the View-based iOS application template there is no such thing, and that this nib specification was not actually needed in the first place, as long as you created your UIViewController subclass with option "With XIB for user interface" checked. My question is, how does XCode know which nib is associated with this controller, i.e. is it storing this connection in some of the files, or maybe by some sort of convention (same name for controller and nib file, perhaps)? Moreover, where does that 'Loaded from "MyViewBasedAppController"' subtitle come from in Interface builder's view of controller within MainWindow.xib? It's definitely not there when I add the controller by hand, so I'm curious to what magic does XCode do behind my back, when I think I'm just selecting a simple code template.
If you look in the target info (double click on the target to bring that up), 'Properties' tab you'll see the name of the main Nib file. The words 'Nib' and 'Xib' are interchangeable for these purposes; Xib is just a newer alternative for Nib.
It'll be 'MainWindow' fresh from the template. If you open MainWindow.xib you'll see that in there is an object called '[project name] App Delegate', and if you show the inspector and check under the 'i' tab you'll see the type of class that is named at the top. If you check the connections tab (the right facing arrow), you'll see that the File Owner (which is the UIApplication itself) has its 'delegate' property attached.
You'll also see that it has an outlet called 'viewController'. That's attached to another object in the xib called '[project name] View Controller'. Check the type on that and you'll see it's the type of view controller that Xcode has added to your project. Looking at its attributes (the first tab in the inspector, with the slider graphic), you'll also see that a separate nib file is specified as containing its main details.
For argument's sake, suppose I called my project 'NibTest' and made no changes.
At runtime, the device loads Info.plist. In there it sees that the delegate is of type NibTestAppDelegate. So it'll instantiate an instance of the class NibTestAppDelegate and set the UIApplication's delegate property to it.
It'll then see from MainWindow.nib that NibTestAppDelegate has a member named viewController of type NibTestViewController. So it'll create an instance of that and set the viewController property on the NibTestAppDelegate instance it just created to it.
In doing that it'll open the other xib and continue doing the same sort of steps.
Objective-C has a fully reflective runtime, so you can instantiate objects by their class name at runtime. This is one of the differences between Objective-C and C++, for example.
Xcode isn't generating any hidden code or relying on any hidden naming conventions. The whole thing is figured out at runtime by the OS.
EDIT: for example, in place of your example:
MyViewController *aViewController = [[MyViewController alloc]
initWithNibName:#"MyViewController" bundle:[NSBundle mainBundle]];
You could actually do:
MyViewController *aViewController = [[NSClassFromString(#"MyViewController") alloc]
initWithNibName:#"MyViewController" bundle:[NSBundle mainBundle]];
They'll operate identically as long as MyViewController exists in the program or in the wider runtime.
You could alternatively pass any other string object you like to NSClassFromString. Even ask the user for it if you want (though it'd be a really bad idea for security reasons).
I'm having this problem because I originially made everything in the main NIB, but then after reading learned that it is better to use subviews.
I've got my IBActions in the AppDelegate, and I've successfully connected and loaded my subviews. However, when I try to connect my buttons in the subviews to the IBActions in the AppDelegate, the IBActions appear under the "First Responder". They seem to connect fine, but when running the application they do not trigger the IBActions (I've confirmed this with some NSLogs, it's not an error in the code within the IBActions). What am I doing wrong?
Thanks!
The AppDelegate should only be used for very specific items such as implementing the UIApplicationDelegate protocol (i.e. methods like applicationDidFinishLaunching) or in some cases storing global variables.
You should keep IBActions and other outlets in their respective view controller files (i.e. if you created MyViewController.h and MyViewController.m which are linked with MyViewController.xib where you have some buttons, images, etc.). They can then be hooked up via dragging the inspector control you want (i.e. TouchUpInside) to the File's Owner.
Something you should read to better understand view controllers: http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html
Typically it is best to create a unique view controller for each view you will present to the user. For instance, if I had a main screen and then an "about" or a settings screen, I would make each of those their own view controller. It helps organize things better than using one view with a whole bunch of subviews that you hide/show and will also improve loading times and general performance.
Update for your 2nd question in the comments about accessing the app delegate:
First, you need to import the .h file (i.e. #import "AppDelegate.h") for the app delegate into whichever view controller .m file you wanna use to access whatever variables, arrays, etc you have stored in the app delegate files. Make sure you synthesize whichever objects you create in the app delegate's .h file in the app delegate's .m file so the getters and setters are created (so you can access them).
Then in the view controller .m file, in whichever method you are using:
-(void)someMethod {
// here we just create a shortcut to the app delegate called "theAppDelegate"
YourAppDelegateFileNameHere *theAppDelegate = (YourAppDelegateFileNameHere *)[[UIApplication sharedApplication] delegate];
// now you can use the dot notation if you wanna access a variable
int SomeNewInteger = theAppDelegate.someIntegerYouHaveStored;
// or some array you have stored
return [theAppDelegate.someArrayYouCreated count];
}
Hope that helps!
I've finally had to give up the relentless search for an answer to this question, as I just can't find anyone that's asked it before! So hope someone can provide some insight. I'll start by explaining what I can do, then compare that with what I can't figure out how to do.
Suppose I have a custom VC called RootViewController. It contains an outlet of type MyViewController. My RootViewController has an .xib which contains a generic VC object dragged out of IB's palette which is given the class type of MyViewController, I set the right bar button item to a UIBarButtonItem called 'Cancel'. The RootViewController unarchives it's .xib, hooks up the outlet to the MyViewController object, I push said object on to the navigation stack which displays as expected, I have a view and a button on the navigation bar that says 'Cancel'.
The problem I have with this approach is that I want MyViewController to be re-usable across any object that might want to create a MyViewController. This therefore means that the "File's Owner" could be any object. Previously it was RootViewController, but what if another object wanted to instantiate a MyViewController? The Files Owner will be different every time. I also want it to be able to completely initialise itself from a nicely self-contained .xib. Basically, I want to do this:
MyViewController *myVC = [[MyViewController alloc] init];
And in the implementation of MyViewController, I write this:
- (id)init
{
if ( self = [super initWithNibName:#"MyViewController" bundle:nil] )
{
// Initialisation
}
}
This neatly hides the name of the .xib used to intialise the VC, uses all the goodness IB gives me in configuring the controller and view, and with MyViewController always being the owner, it means I solve the File's Owner problem too - it will work for any type of class that creates it.
So, in order to achieve this, I create a .xib, set the File's Owner to be of type MyViewController, drop in a UINavigationItem and add the UIBarButtonItem. I now have a .xib structurally the same as before, except it does not use IB's generic VC object as separate top level object, the .xib is the VC definition, rather than something that contains a VC definition.
So, given that File's Owner is a MyViewController and as such a subclass of UIViewController (supposedly inheriting everything you get by using a UIViewController from IB's palette), then I should inherit all the functionality of it's superclass... Except it doesn't.
When the .xib in unarchived, it does not hook up the UINavigationItem. Therefore, when it's pushed on to the navigation stack, none of the bar button items are displayed.
UIViewControllers navigationItem property is read-only, creating an outlet for it in iPhone OS 3.0 is therefore deprecated.
So, at the end of all this, how on earth does the nib loading code manage to connect IB's version of the VC object to the navigation item? And why, even though my object is a UIViewController through inheritance, will it not do it for my object? I am completely at a loss to fathom this out...
Thanks for reading this far! Hope to hear from you guru's
Only a UINavigationController sets UIViewController's navigationItem property. Do something like this:
MyViewController *viewController = [[MyViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
Assuming you are in a view controller that already has a navigation controller. After you have pushed it, you will be able to access navigationItem from inside MyViewController.
Right, I think I get it now. I thought I'd post an answer to my own question as I've seen at least one other person ask a similar question, and both seem to point to the same conclusion, so I hope this will be of help to others.
Essentially (and this will sound obvious) a UIViewController is not a Proxy Object! It's not instantly obvious as we're all used to the idea that if any two objects inherit from the same base class, then their implementations are the same and they will behave in exactly the same way (assuming no customisation in the inheriting class). But an IB object's type is distinct from the class attribute you can assign to these objects.
They are not both UIViewController objects because their class attribute it set in such a way.
Simply setting the class attribute of these objects in IB does not mean that these objects are now UIViewController objects. A View Controller object remains a View Controler object, and a Proxy Object remains a Proxy Object. As far as IB is concerned, they are both very different beasts, they just happen to have the same class attribute.
Just take a look in to the .xib and you'll find your IB View Controller objects have been archived like this:
<object class="IBUIViewController" id="...">
...
</object>
And the Proxy Object (that is set to a subclass of a UIViewController) is archived like this:
<object class="IBProxyObject" id="...">
...
</object>
As you can see they are both very different types, one is a IBUIViewController and the other is a IBProxyObject - and then it starts to makes sense - you can't impose VC controller attributes on an object of type IBProxyObject.
It is also interesting to note the class type is an IBUIViewController object and not just a UIViewController object. Whether this is just a naming convention or not I don't know, but it could also imply that IB's view controller objects wrap the instantiated VC object, or is a factory object for it. For example, you can set a "Resize View From NIB" attribute in IB's view controller object - but I can find no equivalent property or methods in the UIViewController reference docs.
So in conclusion, if you're trying to instantiate an object programatically instead of using outlets to an IB object, be prepared to implement some of the initialisation that the IB version would otherwise provide for you...
I have a UIViewController that is loading several subviews at different times based on user interaction. I originally built all of these subviews in code, with no nib files. Now I am moving to nib files with custom UIView subclasses.
Some of these subviews display static data, and I am using loadNibNamed:owner:options: to load them into the view controller. Others contain controls that I need to access.
I (sort of) understand the reasons Apple says to use one view controller per screen of content, using generic controller objects (NSObjects) to manage subsections of a screen.
So I need a view controller, a generic controller, a view class and a nib. How do I put this all together?
My working assumptions and subsequent questions:
I will associate the view class with
the nib in the 'class identity' drop
down in IB.
The view controller will coordinate
overall screen interactions. When
necessary, it will create an instance
of the generic controller.
Does the generic controller load the
nib? How?
Do I define the outlets and actions
in that view class, or should they be
in the generic controller?
How do I pass messages between the
view controller and the generic
controller?
If anyone can point me to some sample code using a controller in this way, it will go a long way to helping me understand. None of the books or stackoverflow posts I've read have quite hit the spot yet.
Okay, I think I figured it out:
Extend NSObject to make your CustomController
Define your outlets & actons in CustomController.h, including a reference to the UIView in your nib
Set the File's Owner of your nib to CustomController
Hook up all your outlets & actions as usual, including the UIView outlet
In your CustomController.m init, load the nib
- (id)init {
self = [super init];
if (self != nil)
[self loadNib];
return self;
}
- (BOOL)loadNib {
NSArray *topLevelObjs = nil;
topLevelObjs = [[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
if (topLevelObjs == nil) {
NSLog(#"Error! Could not load nib file.\n");
return NO;
}
return YES;
}
The new NSObject based controller will work very much like a view controller.
It sounds like what you want is what I've coined "reusable UIView widgets" -- reusable widgets that do something / present a display that you can incorporate in your app screens wherever and how many times as you want -- you can create them purely in code or instantiate them by placing their frame in another xib file (but you don't get to change the widgets' internal parameters in the xib file, that would require an IB plugin).
This is an organization that I've never seen discussed anywhere. Part of my frustration early on with iOS programming is wanting something like this but not seeing any way to express it in any example.
See my answer in this question to see how it can be structured:
UIView and initWithFrame and a NIB file. How can i get the NIB file loaded?
I recommend placing all widget-internal handling of direct/low-level events in the uiview widget subclass, and implememnt a delegate protocol for a highler level interaction with the widget client (i.e., "loginRequested:userName:passWord" instead of manually accessing button and textfields internal to the widget).
The (optional but recommended) xib file for the widget has an owner of the widget, and the init code in the widget owns the duty of loading the xib file. The customer of the widget simply instantiates the widget and implements whicever widget delegate functions makes sense for it.
In my application,I need to load different .xib in different tableView cells depending upon the category of data which I'm getting from parser. I wanted to ask that is it possible to create different .xibs belonging to same class as it'll reduce the load as I have almost 13 categories so 13 .xib files.
Thanks in advance.
#"I wanted to ask that is it possible to create different .xibs belonging to same class as it'll reduce the load as I have almost 13 categories so 13 .xib files."
The xib files are not a burden on memory unless they are loaded, in which case, the file's owner object is created. So keeping this in mind, it doesnt matter how many nibs you have for your class, for an object of each viewController class, the corresponding xib is loaded. So ultimately you have to put in a check condition as stated by RaYell, it would be better to introduce that check where you spawn the viewController object instead checking the condition for loading appropriate xib.
Dont bother about creating 13 viewControllers, you will find it easier to make changes in your project later if there are changes in requirements. You will appreciate this approach.
If you create only one UIViewController sub-class and load one of 13 xib's based on some condition, say, there comes a requirement that you add a button / label / textField in the 13th xib ONLY and need its reference in your viewController class. How would you achieve it, you maintain an IBOutlet in the common viewController class and introduce the if-else check to see if it is the 13th category. The code becomes untidy with lots of if else conditions.
If you mean that you'd want to have several NIBs for the same view controller then it's most certainly possible. In fact that's how application localization is done. You can then load the specific NIB when you initialize your controller.
NSString *nibName = #"DefaultNibName";
if (someCondition) {
nibName = #"SomeOtherNib";
}
YourViewController *controller = [[YourViewController alloc]
initWithNibName:nibName bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
But how will you make connections(outlets) which will be different in different .xib files?
will you keep lot of outlets and actions in a single controller? If so, then think accidenlty you try to access the outlet which is suppose to be of some other nib. Then what will happen?
If you try to do so then you view controller will look like a garbage. So please dont try to use only one controller for loading more than one .xib files.