Multiple XIBs same File's Owner - iphone

I am trying to create a two view, single controller application as follows: I have two XIB's. Each with the same File's Owner.
As a test, I have placed a UILabel on each XIB. I have connected the File Owner to the UILabel in each XIB. The outlet property is the same.
When I instantiate the nib using loadNibNamed I also set the 'owner' to the instance of File's Owner, e.g.:
nib=[[NSBundle mainBundle] loadNibNamed:#"ONE" owner:OWNER options:nil];
nib=[[NSBundle mainBundle] loadNibNamed:#"TWO" owner:OWNER options:nil];
Now, in OWNER, if I call
[myLabel setText:#"Hello World"];
I see the label update only in nib TWO.
If I create additional UILabels that are unique to each NIB then I can properly update and view them. It seems that I can only have one connection from the property on File's Owner to each NIB.
Any ideas?

What you want is an IBOutletCollection. That allows you to assign a property to more than one nib element, and talk about the entire group all at once.

an IBOutlet can only point to one object. You will need two of every IBOutlet you want to use.

This is very old and not much viewed, but I can't help but notice why this doesn't work. You're passing in the same instance of the owner. Make two instances of the file's owner and you can have two different label values. There's no reason you couldn't assign all of your outlets in your whole project to one Object class in fact, although you probably wouldn't want to do this. Another thing to think about is whether you shouldn't be using inheritance here, by making a superclass, connecting all of the common outlets to that class and then a subclass with unique outlets. Indeed, since you will either have to distinguish your nibs by calling them by nib identifier or by a the class associated with them I think it's better practice to associate separate classes and use inheritance to cover the overlap between them.

Related

Two UIViewController, one XIB File

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.

Put a UIView into a UITableView Header

I have a UITableView which is in my xib file. And I created a property like this for the controller:
#property (nonatomic, retain) IBOutlet UITableView *myTableView;
Now, I want to have a table view header (not a header for each section). So, because I want to have custom styling, I created a new xib file with a view (and I connected to my controller which has the myTableView implemented).
Then I can write in viewDidLoad in my controller:
[self.myTableView setTableHeaderView:self.myTableHeaderView];
where myTableViewHeader is a UIView property in the controller.
Unfortunately, the UITableView won't display this UIView, so my question is, how can I put a UIView to a UITableView into the header?
Thank you in advance & Best Regards.
A couple of things to check:
myTableHeaderView must also be an IBOutlet or created in code in your Class somewhere
self.myTableHeaderView must not be nil when you're trying to add it as the table header view. If it is nil then you didn't hook up your outlets correctly
if you've designed the table view header in IB in its own .xib file, then you must somewhere call this (viewDidLoad is a good place):
[[NSBundle mainBundle] loadNibNamed:#"MyTableViewHeader" owner:self options:nil];
The File's Owner of the MyTableViewHeader.xib must be your TableViewController subclass, and you must hook up the myTableHeaderView object to File's Owner's corresponding outlet.
EDIT: in answer to "what does [[NSBundle mainBundle] loadNibNamed:#"MyTableViewHeader" owner:self options:nil];" do?
This one little line contains the magic that will open your eyes to how XIB files and Objective-C objects work together in Cocoa (touch), elevating your iOS programming kung-fu to entirely new levels. There are two classes of Cocoa programmers, those who understand what it does and use and benefit from it, and those who don't yet know what they're missing, and instead stumble through the wilderness trying to create XIB files for their objects and never quite getting it to work.
With that massive build-up here's the details:
A XIB (NIB) file is a collection of archived Objective-C objects (and reference to objects not actually within the XIB, like "File's Owner", a so-called "proxy object") and connections between these objects. When a XIB file is loaded, these archived objects are brought to life in exactly the state they were saved into the XIB, and then the connections between those live objects (and "proxy objects") are made according to the connections recorded in the XIB file.
For example, in your standard UIViewController subclass .xib file, you have File's Owner set to your MyViewController class. Inside the .xib is a UIView object, which usually itself contains other UIKit objects. The "view" outlet of the UIViewController class is set to point to the UIView object in the .xib. When this .xib file is loaded, the UIView object is unarchived and becomes a living UIView object, with all of the properties and settings recorded in the .xib. That's the "unarchiving part".
And then, because of the connection in the .xib from File's Owner (MyViewController class) to the UIView object, the pointer to this new UIView object is stored in the "view" field of your MyViewController instance. Any other connections also present in the .xib file, like UILabels, UIButton actions, etc., are also set up, to any other "IBOutlet" fields in MyViewController.
This all seems like magic and happens with the
(id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
method when you init a new UIViewController subclass.
Now the good part: you get to do this sort of associating nib files with objects yourself! You can use the loadNibNamed:owner:options method to associate any .xib file with matching set of nil IBOutlets in any object that you want!!!
All of a sudden, creating entirely custom table view cells, table headers, footers, whatever, is a breeze, you can easily write modular reusable UIView components, etc., all laid out in Interface Builder.
The object whose nil IBOutlets you want to fill in with objects loaded from a .xib file is the "owner" object. Usually (but I'm not sure this is absolutely required, any class with the identically typed and named IBOutlets set to File's Owner may work), this is the class that will be specified as "File's Owner" in the xib.
OK, now you've got your existing owner object with nil IBOutlets (the IBOutlets must be nil or they won't be changed, that's the "rule" of loading XIBs into an owner object. It's ok that some IBOutlets are not nil, they just won't be changed when you load the XIB file, and usually that's what you want), and you've got your .xib file with objects that you want to load into the owner object. You call:
[[NSBundle mainBundle] loadNibNamed:#"MyXIBFileToLoad" owner:theOwner options:nil];
.. and voila! Now any nil IBOutlets in "theOwner" that are connected to objects in the MyXIBFileToLoad.xib have been set to the new objects loaded from the XIB file.
(also, this method returns an array of all objects unarchived from the xib. If you don't care about setting any owner's outlets, you can just search this list for your objects by class and by tag).
So that's the story, now go crazy with new ways to associate Objective-C classes with objects stored in XIB files!

Single View controller with multiple nibs?

I am trying to construct a view controller that can be 'skinned' -- that is, have multiple appearances or personalities but that uses a single controller. Each view will have the same buttons, etc, but I would like to be able to load each nib file (skin) into the same view controller. I can create multiple nib files, but I don't see how to connect the buttons, and actions. Can I specify the same 'file's owner' for multiple nib files? (HOW?).
Can this be done?
This is totally possible. Just create new nib files and in Interface Builder set the file owner to the class. You can then hook up your outlets and actions just like before. From your code just specify the correct nib file in the initWithNibName: method.
If the only changes are cosmetic, you might be better off just making those changes in code, but your proposed method will work just fine.
you can do it much easier if you literally copy and paste the view inside the nib file into the same nib file, so that you have 2 separate views inside 1 nib file.
then you can swap between the views as you load the nib like so:
NSArray *temp = [[NSBundle mainBundle] loadNibNamed:#"Widget" owner:self options:nil];
Widget *w = [temp objectAtIndex:0]]; // or 1 or 2 etc to get the different views
this will copy all your button connections etc, so you can just fiddle around with the copy without having to setup everything again

iPhone Having Two Views associated with ONE view controller

Blarg!
I'm trying to develop a game with zelda-like qualities. (i.e. When the PC hits the edge of the screen, the screen changes to the next view, and the PC's position restarts at the appropriate edge.)
The problem I'm having is that I don't want to have multiple View Controllers for each level-segment, because all of the data/functionality exists in the original "LevelView" controller. How do I retain the code from this "LevelView" controller, while only switching NIB files? (i.e. The only classes that I want to be there are the AppDelegate, "LevelView", "ItemView", etc.) I don't want to have to re-create a view controller for each NIB file in the game.
I appreciate any help you can offer! Thank you very much! :D
Create a NIB file that has just the new view in it. Change the class of the File's Owner in the NIB file to the class of the LevelView controller. When you want to load a new NIB file, call the following code from the LevelView controller:
NSArray *topLevelObjects = [[[NSBundle mainBundle] loadNibNamed:#"Put your nib file name here" owner:self options:nil];
UIView *newLevelView = [[topLevelObjects objectAtIndex:0] retain];
What is this doing? When you call loadNibNamed, it returns to you an array of the top-level objects in the NIB file. The "File's Owner" and "First Responder" proxy objects don't count as top-level objects. So, you'll get an array with just the top-level view in the NIB file. You pass self as the file's owner so that any outlet connections you make between the subviews of the top-level view and the "File's Owner" in interface builder get connected.
If you connect the top-level view to an outlet in your view controller, you don't need to do anything with the array loadNibNamed returned. Just ignore it and it'll get released automatically (it gets returned to you with a retain count of 0).
Now, I think doing things this way is a bad idea. I think there are better ways to design your app. For example, it probably makes more sense to create a data file (a property list or XML file for example) that describes the levels than to put all of the levels into NIB files. But, if you really want to do this, the stuff above should get you started.
Use NSUserDefaults and get it from there.

How can I programmatically access UI elements in a NIB without 'wiring' them?

I'm contemplating writing some helper functions to make it easier to do simple changes to the UI elements in my iPhone NIB. Mainly - I want to access a UILabel, or other element, via its Name in Interface Builder. Is this possible? Is there a smarter approach?
Example
Say I want to make a super simple iPhone app that displays 'Hello World'. I start a new project and then open the NIB and drag a UILabel into my view and give it a 'Name' of 'LblMain'. Now, presuming that I've included my handy helper function, I'd like to assign the label some new text something like this:
[helper setText:#"Hello World" forLabel:#"LblMain"];
-or-
UILabel *ObjTmp = [helper getUILabel:#"LblMain"];
ObjTemp.text = #"Hello World";
Now - the trick is that I didn't add a:
IBoutlet UILabel *ObjLblMain;
anywhere in .h file - I'm just accessing that label dynamically - wouldn't that be nice?!
Now, for simple apps, to add some more labels or images, I could drag them into my NIB, assign them names in that element's inspector window, and then immediately access them inside code without the stuttering & hassle of adding them in the .h file.
Motivation
Basically, I'm frustrated that I have to wire every element in my NIB - it's a lot of stuttering and bookkeeping that I'd rather avoid.
I could give a design some naming conventions, and they could generate a NIB without needing to be intimate with the implementation.
Name is 100% not accessible after the object is loaded, something I always thought was odd too.
What is accessible is "tag", if you really want to access an element without defining an outlet you can set the (integer only) "tag" value, and then within the superview that holds the tagged element call viewWithTag: passing in the same integer. Don't forget the default is "0" so use something else.
You can definitely load the NIB programmatically, find all the objects and query them to work out what points to what. Just look at Loading Nib Files Programmatically. But the problem is that the Interface Builder Identity Name isn't exposed outside of IB. So I'm not sure what you would use as the "forLabel" parameter. The "Name" field is just a convenience for the IB document window. It's not a property on NSObject.
It can be done by the element tag:
Lets say you have UIView xib file called "yourView" and inside it there is UILabel that you want to access it without wiring.
Set a tag to the UILabel in "yourView" xib file, lets assume you set UILabel tag to 100.
After loading "yourView" anywhere you can get UILabel without having any wiring by using this code.
UILabel* yourlabel =(UILabel*) [yourView viewWithTag: 100];
//do whatever you want to your label.
I think you can try opening the xib in some external editor as XML and see how the compiler sees it, then you might possibly do the same way
For iOS6+ you can use restorationId instead of tag, to make it more "readable", for example you can set the same name in your nib file and in restoration id.
If you do not want to link all the outlets from your nib to your viewcontroller, you still can access them by searching in your current view subviews tree. Note that subviews arrangement is a tree (the same tree that you can see in your nib file), so you will need to do some recursion if you have nested views.
For example:
UIButton *nibButtonView = nil;
for (UIView *view in [self.view subviews]){
if ([view.restorationIdentifier isEqualToString:#"myNibButtonView"]){
nibButtonView = (UIButton *)view;
}
}
[nibButtonView setTitle:#"Yeah" forState:UIControlStateNormal];
In your nib file you should have a button with a restorationId equals to "myNibButtonView", you can find the restorationId textfield in your identity inspector (third column of utilities)
You may use this if you have a huge number of outlets a you don't want to linked them all.