Does XIB always need a view controller? - ios

If I do an interface in IB must I always need a base UIViewController or can I just skip straight to a UIView ?
As of now I'm doing all my design in obj-c which makes for a bit more busy work.
If I have to use a UIViewController is there anyway to suck the UIView out of it if that's all I want?
I just want to be able to pull out static layouts from XIB instead of putting them together in obj-c.
Any suggestions are appreciated!

UIViewController has that handy initWithNib: bundle: method that makes everything so easy, but there are also ways to get objects from xibs via:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"WhateverItsNamed" owner:self options:nil];
(and the nib array here contains objects which correspond to whatever objects are stored in the XIB file).

No, you don't need a UIViewController if it doesn't serve your needs.
After you have initialized a UINib, use its:
-instantiateWithOwner:options:
to access the nib contents. Make the nib's root object be a UIView (or subclass thereof). If you plan on making connections to File's Owner you will need to set File's Owner to a context-appropriate class.

No, you don't need to associate a UIViewController with a XIB.
First, do not add a view controller to the XIB. Then, you'd often use NSNib or UINib APIs to access the top level objects of the nib -- so you can avoid a view controller and set up any structure you'd like in the NIB editor, and programmatically access the objects in the NIB as needed.

In iOS 5, XIB are not recommended to be used anymore, you should use a storyboard instead.
And to simply answer your question, every top level view requires a view controller to be responsible for it.
All sub views can be an outlet in your view controller and be handled from there.

Related

Connect UIView in XIB and load it using View Controller in the Story board

I have a XYZViewController (simple UIViewController in storyboard) that is loaded up with the default view. I have a type XYZView for which I have UIView in a .xib file.
In the XYZViewController class, I have defined property for XYZView as an IBOutlet. What is tricky is I don't know how to connect this property to the UIViewController in storyboard (or UIVIew in .xib file) such that —
the IBOutlet is connected to the right UIView
the view in the xib becomes an added subview for the default view of the UIViewController.
(I under the question sounds dodgy and/or I may not have the very right way to explain it, but that's the best I could.)
EDIT: Further clarification may make it easier. I just don't want to myself say:
XYZView *xyzView = [[XYZView alloc] initWithFrame...];
or
[self.view addSubview:xyzView];
Maybe that helps.
OK, from what I tell you have the following...
XYZViewController
The code of this is in XYZViewController.h and .m files.
A .storyboard file
In the storyboard file you have a view controller that you have set the subclass to XYZViewController.
A .xib file
In the xib file you have a single view that you have defined as the subclass XYZView.
Right?
I'm guessing what you have done is the following...
In the .xib file you have laid out the XYZView and put labels, buttons, etc... on it.
The view controller is being created by the storyboard. But now you want to attach the labels and buttons to it.
Right?
If all this is correct then you have a couple of options.
The easiest option
Drop the xib file. Unless that XYZView is being used in multiple places in the app (i.e. inside different view controllers) then you should really be doing all of that layout in the storyboard. Add the buttons and labels to the XYZViewController in the storyboard.
This will then allow you to connect the IBOutlets and IBActions and it will all just work because the storyboard is creating and then setting the outlets and actions.
Next option
Because you have created the view in a xib file you have to load it from that xib file in code and then add it to you view controller.
Something like...
- (void)viewDidLoad
{
[super viewDidLoad];
self.xyzView = [[[NSBundle mainBundle] loadNibNamed:#"XYZView" owner:self options:nil] objectAtIndex:0];
[self.view addSubview:xyzView];
}
Then you can do stuff like ...
self.xyzView.someLabel.text = #"This is the text";
You still won't be able to connect outlets and actions but that's because the view controller is not being created by the xib. It's being created by the storyboard.
This can get all messy though. I'd really only recommend creating a view in a separate xib if it's something that you reuse over and over (like a 5star rating view or something).
What you absolutely can't do
OK, I think I may have thought of what you are doing.
In the storyboard you have set the subclass of the view as XYZView and you are expecting it to pick up the labels and buttons etc... that you have defined in the xib file for XYZView.
This absolutely will not work, ever.
The storyboard and the xib are completely separate objects. If you want to use them together then code is involved in loading a view from a nib and then adding it to a view controller created in a storyboard.

How does NSBundle set pointers to view objects in instance variables when loading a nib file?

I'm working through a book and currently adding a view to a tables header programmatically. So I've setup the XIB file added a view resized it and added two subViews/UIButtons.
Interface file:
#import <Foundation/Foundation.h>
#interface ItemsViewController : UITableViewController
{
IBOutlet UIView *headerView;
}
- (UIView *)headerView;
- (IBAction)addNewItem:(id)sender;
- (IBAction)toggleEditingMode:(id)sender;
#end
Part of my implementation:
- (UIView *)headerView
{
// If we haven't got the headerView yet
if (!headerView) {
[[NSBundle mainBundle] loadNibNamed:#"HeaderView" owner:self options:nil];
}
return headerView;
}
In the Big Nerd book it states:
“The first time the headerView message is sent to the ItemsViewController, it will load HeaderView.xib and keep a pointer to the view object in the instance variable headerView.”
What I'd like to know is how is how it sets/keeps a pointer to the view object in the headerView instance variable?
I know the nib file is being is being loaded by passing it into the loadNibName parameter as a string but how is the pointer being set to the object in my headerView instance variable?
When I connect the file owner to headerView is that like creating an instance of UIView and storing it in the headerView instance variable programmatically?
I found it really hard to word the question but I hope it makes sense after reading this post.
Kind regards
The -loadNibNamed:owner:options: method on NSBundle will read a nib file, instantiate the objects defined in it, and set any connections the nib file defines between those objects or between the loaded objects and the available proxy objects (like "File's Owner"). In this case you are passing self (the current ItemsViewController instance) as the owner argument. That ItemsViewController will then be the "File's Owner" when objects are loaded from this nib file.
If you look at the nib in Xcode you should see that one of the UIViews defined in that nib is connected to the headerView outlet of the "File's Owner". As a result when the nib is loaded it will attempt to set that headerView ivar to reference the loaded UIView object.
When I connect the file owner to headerView is that like creating an instance of UIView and storing it in the headerView instance variable programmatically?
Yes, this is equivalent to creating the view programmatically and setting the ivar yourself. You could write a method to create views on demand instead of using a nib as a factory for view objects if you prefer. Nibs are handy because they can allow you to configure view properties in a visual editor which may be easier to adjust than setting those properties progammatically. Nibs also offer a way to loosely couple the source of your view instances to the controller using those views which is convenient when you might want to switch which view your controller uses (iPad vs iPhone view, or different views for different locales).

Is there a way to 'design' a custom UIView in storyboard, and use in multiple ViewControllers without adding to any specific controller?

I'm in need of a custom UIView which is to be used in multiple different ViewControllers.
Usually when I create a custom UIView, I drag an instance of UIView into my ViewController in storyboard, create a subclass of UIView, and point to this subclass in the Identity Inspector for the view. From there, I would connect the UI-objects as outlets to the header-file of the subclass.
The view I want to make now, is not supposed to be a part of any specific controller, but should be added to any controller that asks for it. A rogue UIView. I know I can create the entire UIView programmatically, and just create an instance of it, but I'd like to have it (and design it) in my storyboard.
In Storyboard, the only objects I'm allowed to drag 'outside a ViewController', are other ViewControllers.
I have never used anything other than Storyboard for iOS-developing, but I have come over tutorials which have been using the other mode, from the olden days, which looks like what I need. Is it possible to get something similar into my storyboard? Or would this require its own setup/design? If so, how?
I was also thinking of solving this with adding a 'phantom' UIViewController containing my custom View; designing it there, but instantiate it from the other controllers, but this sounds so hacky..
I'd like to do this with a UITableViewCell as well, but I guess that would have the same solution.
For your UIView, you should be creating a custom UIViewController in your storyboard and instantiate the view controller to access the view:
MyViewController *myViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MyViewController"];
// Access the view using "myViewController.view"
This is a very common practice in iOS since storyboards were presented. If you are using multiple storyboards, you should create a new instance of UIStoryBoard with:
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
And then instantiate the view controller with this instance of the storyboard.
If you want to solely use a UIView, you should be creating a .xib file, i.e. the olden days format. To create a custom UITableViewCell, I would absolutely use a .xib file. Your last option would be to create a UIView programmatically, which could be called from any place in your application. Depending on the complexity of this view, this may be a valid option.
I don't think you can create a custom UIView in storyboard. So in this case, you can create a xib file for that custom view. Then when you want use that view, just use
UIView *customView = [[[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil] objectAtIndex:0];
to get that view.

reusable ui components using nibs

I like using interface builder to design ui elements but am designing a screen in which different subviews display depending on conditions. For me its easier to design those subviews in seperate nib files and then load them into the view controller when needed.
e.g. I have the following activity view that is lazy loaded that is basically an overlay that displays an activity indicator, I would like the option to reuse this in different view controllers.
Here is the getter method in one of the view controllers
- (ActivityView *)activityView
{
if (!activityView) {
activityView = [[[UINib nibWithNibName:#"ActivityView" bundle:nil]
instantiateWithOwner:self options:nil] lastObject];
}
return activityView;
}
In the nib file I do not set the owner to match the view controller as I would like multiple view controllers to be able to use it, but I link the top level view to an ActivityView class so I can customize some of its ui programatically.
This approach seems to work for me but I wonder if this is a good approach?
In the resource programing guide it says:
Important: You are responsible for releasing the top-level objects of any nib files you load when you are finished with those objects. Failure to do so is a cause of memory leaks in many applications. After releasing the top-level objects, it is a good idea to clear any outlets pointing to objects in the nib file by setting them to nil. You should clear outlets associated with all of the nib file’s objects, not just the top-level objects.
I was not sure if this was still applicable if I have ARC and what I would need to do about it?
Does this just mean I need to set self.activityView = nil in the didReceiveMemoryWarning method?

Custom Sized Nib File Controllers and Interface Builder

I've recently been introduced to development for iOS by a friend and have begun to experiment with the interface builder and view controllers. One thing I'm finding is that when using a nib in conjunction with a view controller, your view controllers ivars are quickly polluted with views you may never actually reference. I would like to modularize the key components in my main nib into several different views. I have two questions regarding this:
How can I create a nib file for a custom sized view (one that doesn't fill the entire screen)?
How can I add the newly modularized to my main nib (all of the classes I would create for the components would be view controllers not views)?
Assuming I were to alloc and init the view controllers and add them to the main view programmatically, how could I position the custom sized views since I have already called initWithNibName:bundle:. I can't call initWithFrame: right?
Answers would be much appreciated!
Thanks,
PhpMyCoder
EDIT
I've discovered the answer to my first question. It seems that in the attributes inspector of your nib file you must disable the status bar (change it to unspecified) to enable the editing of the height and width parameters in the size inspector. However, I still am unsure of how to add these custom nibs and their view controllers to another nib without coding them in with initWithFrame: and addSubview:. Any ideas on adding a view controller's view to a nib in IB?
EDIT 2
Added question 3 (or 2 depending on how you think about it).
EDIT 3
I seem to be hastily asking questions today. A simple call to setFrame: will deal with sizing and positioning (and you can even append it on to your init function initWithNibName:bundle:frame:). Still not sure how to add the view (created by a nib) from a view controller to another nib in Interface Builder. If this is possible, I'd love to hear how.
Remember to not get ViewControllers and Views confused.
They are both in a hierarchy, and each ViewController has/controls a "main" view (which likely has bunches of subviews).
You don't add a view to a nib. A nib is a mechanism to help you assemble views. (A NeXT Interface Builder file, if we delve into nomenclature.)
This is how you load a nib:
[[NSBundle mainBundle] loadNibNamed:#"foo" owner:self options:nil];
Normally, you give a nib name to a controller and it does it for you, but you can do this to. There are complex and tricky ways to access the content. The following is the standard way to do it.
The owner you pass in must be the type declared as the owner in that nib file. It should have some of its outlets connected to objects in the nib file. After you load the nib file, they'll just "be there". If you called this twice, it would overwrite the first ones and replace them with the second ones. (mostly harmless, definitely useless)
So, typically, you wired it up to view. Now you have a view that's floating around in memory and not connected to the view hierarchy of the application. Time to do that. You must take view and figure out where it belongs in the pre-existing hierarchy and call [someOtherView addSubview:self.view] no it, and it will appear. Yes, if you want to explicitly place/size it, you will need to do that. Note that view.frame is in the superview's coordinate system.

Resources