The default init method signature on XCode-generated view controllers is:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ }
I've seen these initialized with both values supplied, just the nib name (with bundle as nil), or just nil as both. All seem to work.
How does the UIViewController really handle self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];? Is there a disadvantage to just passing in nil for both values?
If you pass nil as the nibName, the method will look for a nib with the same filename as your view controller.
For instance, if you have a view controller called MyViewController it will look for a MyViewController.xib nib file.
If no nib is found, you will need to override the loadView method to create and assign a UIView to the controller's view outlet.
- (void)loadView
{
UIView *theView = [[UIView alloc] ...
// Setup the main view
self.view = theView;
}
From the docs:
nibName:
If you specify nil for the nibName parameter and you do not override the loadView method, the view controller searches for a nib file using other means. See nibName.
nibBundle:
The bundle in which to search for the nib file. This method looks for the nib file in the bundle's language-specific project directories first, followed by the Resources directory. If nil, this method looks for the nib file in the main bundle.
Related
I have a custom UIView object with a nib that defines the view/subview layout. My outlets are connected, and when I init the object from initWithFrame: everything is in order.
The problem I'm having is when I'm using IB to embed the UIView into another nib; the custom UIView appears, but none of the subviews it contains appear - in fact their symbols all resolve to nil. I have a very minimal initWithCoder: and awakeFromNib: implementation (just does logging) and my understanding is that as the nib is deserialized the subviews should at least be initialized during the process?
The only conclusion I'm coming to on my own is that one of two things is happening: either my outlet references are corrupt/bad and aren't working, or my understanding of load-from-nib process is faulty and I'm missing something.
Thanks in advance!
Edit: (Code posted for Nekto as requested... as you'll see, it does logging and thats it, heh.)
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
NSLog(#"ThumbnailGridView.initWithCoder frame= %f, %f", self.frame.size.width, self.frame.size.height);
}
return self;
}
- (void)awakeFromNib
{
NSLog(#"ThumbnailGridView:awakeFromNib");
}
Edit 2: Nibs, controllers, subviews, etc.
Nibs: I have a single nib containing a UIView. This UIView contains a single UIScrollView, which is filled with a grid of UIViews that are defined in another nib. (Note: this part works fine, as the fill is done programmatically and works with an initWithFrame: call.) The problem here is that the UIScrollView symbol is nil after initWithCoder: and awakeFromNib: are both called, so objects are just being added to nil and nothing happens. If the scrollview symbol was not nil, I'm sure this would work.
Controllers: There are two controllers that utilize this, one is done with initWithFrame: and works perfectly, the other embeds it as a nib-based reference. (As mentioned elsewhere here, defining a UIView in IB, setting the custom class.) I stepped through the code, and that view -is- being initialized properly - only its subviews are "missing".
Does this help give a clearer picture of the situation at all?
You may be misunderstanding how nib loading works. If you define a custom UIView and create a nib file to lay out its subviews you can't just add a UIView to another nib file, change the class name in IB to your custom class and expect the nib loading system to figure it out. You need to modify initWithCoder of your custom UIView class to programmatically load the nib that defines its subview layout. e.g.:
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
[self addSubview:self.toplevelSubView];
}
return self;
}
Your custom view's nib file needs to have the 'File's owner' class set to your custom view class and you need to have an outlet in your custom class called 'toplevelSubView' connected to a view in your custom view nib file that is acting as a container for all the subviews. Add additional outlets to your view class and connect up the subviews to 'File's owner' (your custom UIView).
Alternatively, just create your custom view programmatically and bypass IB.
Check that you are calling the 'super' implementation of initWithCoder: and awakeFromNib in each of your overridden methods i.e.
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super initWithCoder:decoder])) {
...your init code...
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
...your init code...
}
My question is: i don't understand that we can create a ViewController with a nib file and we can create it without a nib file. i mean that : for example can anyone explan me the template, Navigation based application how it work, what is the first object instanciated ?
thanks for your answers
The app's Info.plist file contains a property called "Main nib file base name" (NSMainNibFile). The nib file that is set here ("MainWindow.xib" by default) controls what will be loaded at startup.
If you don't have that set, and you want to launch an application without a default nib file, you need to pass in the name of your app delegate in your main.m file.
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
http://blog.hplogsdon.com/ios-applications-without-any-nib-files/
the only method to instantiate a UIViewControler is:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
that mean you "normally" ask it to load a .nib file...
but you can also pass "nil" to both parameters:
myUIViewController = [[MyUIViewController alloc] initWithNibName:nil bundle:nil];
...if you want to load it directly and manage it by yourself. Generally you crate a subClass of UIViewController (MyUIViewController in my sample) and in its #implementation you implement the method loadView
where you need to create the view of your class
- (void)loadView{
UIView *aUIView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320, 480)];
self.view = aUIView;
aUIView.backgroundColor = [UIColor colorWithRed:.2 green:.3 blue:.5 alpha:1];
// aUIView... other properties to set if needed...
[aUIView release];
}
this way you can manage it all without a ".nib file", adding all objects and subView only via code...
I make all my controllers in code and most of the GUI too. Some GUI's I make with IB. I then set the file's owner to the viewcontroller and drag an connection from the file's owner to the view. But initWithNibName confuses me...
I am override the designated initializer to this
- (id)init {
[super initWithNibName:nil bundle:nil];
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
return [self init];
}
Why do I not need to set which nib the viewController shall use in the init-initalizer? Because it works without. I thought I must use [super initWithNibName:#"SomeNib" bundle:nil];
In the init-initalizer
From docs: If you specify nil for the nibName parameter, you must either override the loadView method and create your views there or you must provide a nib file in your bundle whose name (without the .nib extension) matches the name of your view controller class. (In this latter case, the class name becomes the name stored in the nibName property.) If you do none of these, the view controller will be unable to load its view.
It works without only if your nib name is the same as your controller class name. In that case Apple does some magic. It is generally good form to specify the nib name.
I am a little curious, I have a view controller class and an NIB/XIB (both are named "MapViewController") If I do the following it loads the NIB with the matching name.
-(id)init {
self = [super initWithNibName:#"MapViewController" bundle:nil];
if(self) {
do things ...
}
return self;
}
if on the other hand I just specify [super init] does Xcode just look for a NIB that matches the name of the controller, is that how this is working?
-(id)init {
self = [super init];
if(self) {
do things ...
}
return self;
}
cheers Gary.
From the documentation:
If you specify nil for the nibName
parameter and do not override the
loadView method in your custom
subclass, the default view controller
behavior is to look for a nib file
whose name (without the .nib
extension) matches the name of your
view controller class. If it finds
one, the class name becomes the value
of the nibName property, which results
in the corresponding nib file being
associated with this view controller.
Yes, in this particular case it will work. According to the UIViewController, calling init is similar to calling initWithNibName:bundle: with nil as nib name:
If you specify nil for the nibName
parameter and do not override the
loadView method in your custom
subclass, the default view controller
behavior is to look for a nib file
whose name (without the .nib
extension) matches the name of your
view controller class. If it finds
one, the class name becomes the value
of the nibName property, which results
in the corresponding nib file being
associated with this view controller.
does Xcode just look for a NIB that
matches the name of the controller
Pretty much:
If you specify nil for the nibName
parameter and do not override the
loadView method in your custom
subclass, the default view controller
behavior is to look for a nib file
whose name (without the .nib
extension) matches the name of your
view controller class. If it finds
one, the class name becomes the value
of the nibName property, which results
in the corresponding nib file being
associated with this view controller.
I have two NIB's
ParentViewController.xib
ChildViewController.xib
ParentViewController.xib contains a UIView and a UIViewController.
ChildViewController.xib contains a UIButton
I want ChildViewController.xib to load in the ParentViewController.xib's UIView
I have done the following:
Created #property for UIView in ParentViewController
Connected File's Owner to UIView in ParentViewController
Set UIViewController in ParentViewController's NIB Name property to ChildViewController in Interface Builder
Set ChildViewController view property to UIView in ParentViewController
I was hoping this would load ChildViewController into my UIView in ParentViewController but no luck.
I did get the following warning, which could be the culprit:
'View Controller (Child View)' has both its 'NIB Name' property set and its 'view' outlet connected. This configuration is not supported.
I also have added additional code in ParentViewController's viewDidLoad():
- (void)viewDidLoad {
[super viewDidLoad];
ChildViewController *childViewController = [[ChildViewController alloc]initWithNibName:#"ChildViewController" bundle:nil];
childViewController.view = self.myView;
}
Any thoughts on why ChildViewController does not load in the UIView of ParentViewController?
Try this
[self.myview addSubview: childViewController.view];
instead of
childViewController.view = self.myView;
The alternative is to build the "embedded" view (!) in a Nib, say ChildView.xib, then instantiate it in the ParentViewController.xib (change the class in the Identity inspector). There is no need to programmatically [self.view addSubview:embeddedView] in the parent view controller's -viewDidLoad method.
I wrote up how we embed custom-view Nibs inside other Nibs in a longish blog post. The crux is overriding -awakeAfterUsingCoder: in the ChildView class, replacing the object loaded from the "parent" Nib with the one loaded from the "child" Nib.
Note that our custom controls subclass UIView, not UIViewController (see Apple's docs on Custom view controllers: "You should not use multiple custom view controllers to manage different portions of the same view hierarchy.")