Single View controller with multiple nibs? - iphone

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

Related

get IBOutlets from xib file

In my nib file I have several controls that I placed with xcode. Is there a way I can find how many outlets there are in the nib file. since I placed all the objects in the default view of the nib file maybe I can get the child of the default view and those will be the IBOutlets. I plan on latter adding functionality to those IBOUtlets.
In short I am trying to create a connection with code.... Thats cause I have so many objects in every nib file. I am creating an app that is like a power point presentation and I have several slides..
IBOutlets arent' actually anything. That is just syntactical sugar to allow Interface Builder introspection into your code files to match up variable names.
Anyways, you can just look through all the subviews of your view in code if you need to. Not ideal, but i think this is what you are asking for.
SubclassedViewController *controller = [[SubclassedViewController alloc]
initWithNibName:#"SubclassedViewController" bundle:nil];
// actually, [[... alloc] init]; does the same
for (UIView *aView in [controller.view subviews]) {
// do stuff
}
You could identify the Views with tags, an integer properties that you can attach to any UIView (also in Interface Builder).

Using a xib file without initializing a view controller

I'd like to use IB to create a toolbar with assorted toolbar items, but create and use this toolbar programatically - adding it to a view at my discretion, and not while initializing the view controller.
I'm pretty sure it can be done - but haven't been able to find a good reference. Any help?
Read about NSBundle's loadNibNamed:owner:options: method:
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:#"Toolbar" owner:self options:nil];
This will return all the objects in nib which you can get using simple array methods. Also note that if nib file references any outlets to other objects in nib - they must be defined in your controller (or any other given nib owner) as well otherwise you'll get KVC exceptions.

How do I get a view in Interface Builder to load a custom view in another nib?

I am making a custom widget that I would like to use in multiple nibs. So I make a new view nib Screen3, add some buttons, and now want my UIAwesomeSauce widget.
If I just add a view and then change the Class Identity, it doesn't get the subelements from the UIAwesomeSauce nib. Same thing if I go to the Library and switch to Classes. It seems only a UIViewController has the field for "Load from nib", which would be beautiful.
I know I can load the UIAwesomeSauce nib from code, get the top level objects, and place it by hand. But the point of IB is so you don't have to place things in code. Even better would be if I could get UIAwesomeSauce to show up in the Library list itself.
SOLVED - by Nimrod - READ ON FOR EXPLANATION AND CODE
Anyway, dood, that is great. I can make my own widget classes now for goofy Awesome stuff. Make the FileOwner of your UI nib your class, and in it just have a normal UIView with all your stuff. (The single view in the widget's nib can't be the class itself, or you get recursive in initWithCoder.) Then in the nib you want to use it, make a vanilla UIView and change its class. You won't be able to actually see the widget inside that square, but deal.
Self is now a blank view, and tMyActualSelf is the single view that you did the work in in the other nib. Yay!
- (id)initWithCoder:(NSCoder*)coder
{
if ((self = [super initWithCoder:coder]))
{
UIView *tMyActualSelf = nil;
// Initialization code
NSArray *tNibItems = [[NSBundle mainBundle] loadNibNamed:#"UIAwesomeSauce" owner:self options:nil];
for (id object in tNibItems)
{
if ([object isKindOfClass:[UIView class]])
tMyActualSelf = (UIView *)[object retain];
}
if( tMyActualSelf )
{
tMyActualSelf.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
[self addSubview:tMyActualSelf];
}
}
return self;
}
I feel your pain when it comes to the UIViewController being the only thing you can specify a nib load with, but I think it's because there has to be a File's Owner that's a controller, or something like that, though I'm not sure why a UIView can't be a File's Owner.
I think you can put things in the library under Custom Objects but I think you'd be limited to putting a UIView in there with the class just set to a custom subclass of UIView. I actually looked into trying to extend interface builder and it looks like you can do it for OS X stuff, but not for iPhone stuff. It involves compiling code for the new widget in interface builder to tell it how to draw the widget just within IB. I think there's actually an OS X template for this.
I think what I'd probably do is create a main view in a nib file that contains all the subviews, then in initWithCoder: in the UIAwesomeSauce thing open the nib file, load the main view, and just place it inside self so that you have an "unnecessary" view between the UIAwesomeSauce view and the subviews (or sub subviews as the case would be.) I'm not sure how inefficient that is, but in messing with some stuff it looks like some views like UIWebView seem to have one of these seemingly unnecssary intermediate views (IIRC).
If you come up with a better solution please tell me because I'd like to know too.
EDIT
Now that I look at it, I think the UIView CAN be the file's owner, you just can't set it up in IB. Basically you'd call the nib load something like this in the UIView subclass:
[bundle loadNibNamed:#"UISpecialSauce" owner:self options:...]
I think if you create outlets in UISpecialSauce or whatever then they should be assigned correctly because of the owner:self.
Theres a cocoapod that might actually help with that:
https://github.com/mobilejazz/NibWrapper
It provides a wrapper view that loads a child view from another NIB. You can specify the name of that NIB inside interface builder via a runtime attribute.

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.

Multiple XIBs same File's Owner

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.