ViewController with multiple views from nibs at a time - iphone

I am in the process of developing an IOS rpg. This game is a controlled by a tab bar, and every view controller in the tab bar will have a common "header" that sits at the top of the screen and shows information about the player.
The rest of the screen, however, will show one of many different views. Each view controller will be responsible for showing multiple different "views" underneath the "header" view. In addition, many of these views will need to be scrollable, as they will not fit in the confines of the screen.
Questions:
1)How do you add two views from separate nibs to a single view controller.
2)How do you embed only one of those views in a scroll view.
Thank you.

You can load a nib through the loadNibNamed:owner:options: function on a NSBundle. What it will return is an array of all the objects in the nib (the list you see on the left when you create a nib in interface builder). If you're view is the first item on the list of objects in the nib, then its the object at the 0th index of that array.
NSArray *objects1 = [[NSBundle mainBundle] loadNibNamed:#"View1Nib" owner:self options:nil];
UIView *customView1 = [objects1 objectAtIndex:0];
NSArray *objects2 = [[NSBundle mainBundle] loadNibNamed:#"View2Nib" owner:self options:nil];
UIView *customView2 = [objects2 objectAtIndex:0];
UIScrollView *scroll = [[[UIScrollView alloc] init] autorelease];
[scroll addSubview:customView2];
[[self view] addSubview:customView1];
[[self view] addSubview:scroll];

If I have multiple views in a nib I make use of the restoration identifiers rather than relying on the order of the array and perform the following:
UINib *nib = [UINib nibWithNibName:#"Nib" bundle:nil];
NSArray* views = [nib instantiateWithOwner:self options:nil];
assert(views.count == 3);
UIView *aView;
UIView *anotherView;
UIView *yetAnotherView;
for (UIView* view in views) {
if ([view.restorationIdentifier isEqualToString:#"AViewId"]) {
aView = (SettingsCell *) view;
}
else if([view.restorationIdentifier isEqualToString:#"AnotherViewId"]) {
anotherView = (SettingsCell *) view;
}
else if([view.restorationIdentifier isEqualToString:#"YetAnotherViewId"]) {
yetAnotherView = (HeaderView *)view;
}
}
assert(aView && anotherView && yetAnotherView);

When you make a view controller, if you choose to generate an xib automatically, its view outlet will, by default be connected to a view. Now, create a new xib, with some different name, and make its files owner as your view controller class. Also, manually connect the view outlet.
Now, call the init method:
YourViewController *x = [[YourViewController alloc] initWithNibName: #"yourNibName" bundle:nil];
according to whatever xib you want to load, place the name instead of "yourNibName". Hope that helps.

You'll need to have references to both views, and you can simply [view addSubview:secondView]; as normal. As for how you get a reference to the views in the xib that is not associated with your view controller, there are several ways, but which you choose will depend on whether that view is already instantiated elsewhere in the app. I'm betting you're already instantiating that view elsewhere, and you simply want to add it. If it were me, I would use a singleton for that view's parent, so I could do something like:
[view addSubview:[ParentClass parentClassSharedInstance] viewToAdd]];
Scroll views are a beast you'll need to work with to fully understand, but you add views to them just like any other view. The important bit is that they have a contentSize property that can be bigger than their frame's size. I usually use a single view of the size I want to manage all views underneath the ScrollView. Good luck!

Related

Two nib file and one view controller

In my project i have created two UIView one for landscape mode and the other for potrait mode. I am using the same same UIViewController to control both these views. These two views have the same content, only thing is that when i switch from one UIView to another the value of the UIControls is not retained.
I have loaded the UIViewenter code here using the following code
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"UIViewName"
owner:self
options:nil];
UIView *lview = (UIView *)[nibArray objectAtIndex:0];
lview.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Background.jpg"]];
self.view = lview;
Can anybody pliz help me retaining the values during orientation change?
You can stock all the values on a NSMutableDictionary in your controller and then put the inputs of the user on the reloaded views when the user changes the orientation.
Look here at theh documentation to see how NSMutableDictionary works:
NSMutableDictionary Documentation

How to get a UIView from a UIViewController without adding the controller to the view hierarchy and making it visible?

I want to avoid laying out a view programmatically.
I want to only use a NIB file to do such work.
The problem is that the only way that I know of bringing a NIB file to life is via a controller like so:
ZBarReaderViewController* reader = [[ZBarReaderViewController alloc] init];
ScannerOverlayViewController *sovc =
[[ScannerOverlayViewController alloc] initWithNibName:#"ScannerOverlayView" bundle:nil];
reader.cameraOverlayView = sovc.view;
But this approach doesn't work out so well because stepping through the debugger shows that the views is 0x0 or Nil.
Now this may be because the controller is never added-to/displayed-on the view hierarchy and I'm trying to get a view out of it in order to recycle it as an "overlay view" on another controller that allows me to specify it.
Any thoughts on how this can be accomplished without writing the View programmatically?
You can load view from XIB file:
// I assume, that there is only one root view in interface file
NSString *nibName = #"CustomView";
NSArray *loadedObjects = [[NSBundle mainBundle] loadNibNamed:nibName owner:nil options:nil];
UIView *view = [loadedObjects lastObject];

TouchesBegan on a Sub-ViewController not getting called

I have a ViewController that responds to some touchEvents (touchesBegan, touchesMoved, etc...).
I've found that when I show this controller using presentModalViewController: it works just fine, but I'm trying to add it's View as a subview of another ParentViewController like so:
- (void)viewDidLoad
{
[super viewDidLoad];
//Add SubController
controller = [[SubViewController alloc] initWithNibName:#"SubViewController" bundle:nil];
controller.view.frame = CGRectMake(0, 30, 300, 130);
[view addSubview:controller.view];
[controller release];
}
When I do this, it gets added the parent view but it no longer responds to touch events. Is there a way to fix this?
Also, is there a better way to go about this? I know I probably could have used a View subclass for the child view, but it's supposed to use a Nib and I wasn't sure how to handle that without using a ViewController.
You're correct you should use a UIView subclass.
The easiest way to load it from a nib is to include the subview in your nib.
Just drop a UIView into the view connected to the original view controller.
Then with the view inside selected go to the identity inspector. It's the one that looks like a little ID card.
The very first field is called Custom Class.
Type the name of your UIView subclass here.
If you need a reference to this just create an IBOutlet in your original view controller and hook it up. That way you can set hidden = YES until you need it.
In your UIView subclass you might want to override
- (void)awakeFromNib
This will get called when the nib first unpacks.
for setting up any gesture recognizers, etc.
To load a nib directly into a view :
// Get the views created inside this xib
NSArray *views = [NSBundle mainBundle] loadNibNamed:#"myViewNib" owner:nil];
// There's probably only one in there, lets get it
UIView *myView = [views objectAtIndex:0];
// Do stuff . . .
[[self view] addSubview:myView];
You could try to call becomeFirstResponder in your subview and see whether it receives touchesBegan... It is probably so, but it will also possibly make the superview not receive touchesBegan if you require it...

Nib is automatically being added to the screen

I have a nib file I'm trying to instantiate in code. My UIViewController's main view is also loaded from a nib file.
Here's my UIViewController's viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *nib = [UINib nibWithNibName:#"MyCustomView" bundle:nil];
NSArray *nibViews = [nib instantiateWithOwner:self options:nil];
MyCustomView *myView = [nibViews objectAtIndex:0];
myView.frame = CGRectMake(100.0f, 100.0f, 91.0f, 91.0f);
[self.view addSubview:myView];
}
This creates some sort of endless loop. If I comment out [self.view addSubview:myView], myView appears, but everything currently on the screen disappears. I didn't think instantiateWithOwner added the view to the screen. If it does, how do I get access to it?
Thanks for your help.
The instantiateWithOwner method reassigns the properties of your view controller (set from the nib the controller was created from) to ones from the new nib. Those properties likely include the view property, so that method, within it, contains a call to setView:, and sets the view controller's view to the new nib's view. Afterwards, you're trying to add a view as a subview to itself, and that, naturally, causes problems.
You want to create your own property, for instance, secondaryView, set the nib's view to that, and add it as a subview. You don't want to reassign your view controller's view.

xib-specified location moves on UIView load?

I have a couple of view controllers that I want all to share a header status bar. This header view shows status of certain things in my app. I used IB to lay out the contents of the header view in its own xib, then also used IB to layout the view controllers, adding a placeholder UIView element in the view controller's xibs for when I load in the header programmatically.
Now, when I create a UIView subclass from the contents of the view xib, and add that as a subview to the view of any of my view controllers, the location that I specified for the header view in my view controller xib seems to get ignored, and the header view gets placed at (0,0) no matter what. All the contents of the header view load and appear fine, it's just the location of the header view in the view controller that is wrong.
Here's how I'm loading and adding my header view to my view controller:
header = [InfoHeader loadFromNib:MY_NIB_NAME withOwner:self];
[self.view addSubview:header];
The view controller xib is confirmed to have an outlet connected to the 'header' variable. And here's that 'loadFromNib' method:
+(InfoHeader *) loadFromNib: (NSString *) nibName withOwner:(id) objectOwner
{
NSBundle *b = [NSBundle mainBundle];
NSArray *nib = [b loadNibNamed:nibName owner:objectOwner options:nil];
InfoHeader *header;
for (id obj in nib)
{
if ([obj isKindOfClass:[InfoHeader class]])
{
header = (InfoHeader *) obj;
}
}
return header;
}
Any ideas on what to check here? I'd rather not locate the header bar programmatically, but let IB do it.
Did you change the location of your subview in IB? Usually, the default location (0, 0) of the view will be the (top, left) coordinate of the big view in the screen. I think checking that will work fine
You can do this without using a "placeholder" view at all, by programmatically specifying the frame of the subview:
InfoHeader *header = [InfoHeader loadFromNib:MY_NIB_NAME withOwner:self];
header.frame = CGRectMake(0, 44, header.frame.size.width, header.frame.size.height);
[self.view addSubview:header];
(that code asssumes that you have removed the header outlet from your class, so it can be a local variable instead).
Or, if you'd rather use the placeholder (so you can specify the header's position using IB instead of programmatically), do it like this:
InfoHeader *headerView = [InfoHeader loadFromNib:MY_NIB_NAME withOwner:self];
[header addSubview:headerView];