I've noticed that there are two different ways to load nib/xib files:
via the UIView's initWithNibName:bundle: method
NSBundle's loadNibNamed:owner:options: method.
Can someone explain the differences between these two and when it is more appropriate to use one over the other and in what circumstances?
For instance, if I'm loading a custom table section header view from a nib file in the tableView:viewForHeaderInSection: method, which one would I use?
Or, if I were loading a custom table view cell from a nib file, which one would I use?
NSBundle’s methods are the generic API to use for unarchiving NIBs. They do the actual work (together with NSNib).
UIViewController’s initWithNibName:bundle: on the other hand is a way to initialize a view controller which (might) load its view from a nib. The method does not itself load the nib but just takes note of the name. The controller loads the nib lazily when the view is requested.
I’m not aware of any nib loading in UIView.
If your header's view controller contains IBOutlets to any fields in the nib file it will be better to load the nib file instead of calling initWithNib.
In the view controller of the header file modify the initiation statement as the following .The default statement is commented out.
By doing so you will be able to access the fields in the nib file using the IBoutlets.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
//self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
// Custom initialization.
if ([[NSBundle mainBundle] loadNibNamed:#"NibFile" owner:self options:nil]) {
}
return self;
}
Related
How do I prevent a UIVIewController from loading the xib?
I downloaded an XCode (iPhone) project online, and I want to stop the xib file from loading. The view did load method doesn't have any code in it which deals with xib. How do I force the UIViewController to be loaded from it's viewDidLoad method instead of the xib?
Overwrite loadview but DO NOT call [super loadview]. Example:
-(void)loadView{
self.view = [UIView new];
[self.view setBackgroundColor:[UIColor redColor]];
}
From the Documentation
loadView
Creates the view that the controller manages.
You should never call this method
directly. The view controller calls this method when the view property
is requested but is currently nil. If you create your views manually,
you must override this method and use it to create your views. If you
use Interface Builder to create your views and initialize the view
controller—that is, you initialize the view using the
initWithNibName:bundle: method, set the nibName and nibBundle
properties directly, or create both your views and view controller in
Interface Builder—then you must not override this method.
The default implementation of this method looks for valid nib
information and uses that information to load the associated nib file.
If no nib information is specified, the default implementation creates
a plain UIView object and makes it the main view.
If you override this method in order to create your views manually,
you should do so and assign the root view of your hierarchy to the
view property. (The views you create should be unique instances and
should not be shared with any other view controller object.) Your
custom implementation of this method should not call super.
If you want to perform any additional initialization of your views, do
so in the viewDidLoad method. In iOS 3.0 and later, you should also
override the viewDidUnload method to release any references to the
view or its contents.
Write your own init method. Later if you require the nib you can create a UINib object and when you need the view you can use instantiateWithOwner.
Using init when creating your view controller will prevent the nib loading. Another thing to do is to name the nib something other than the name of the view controller - because the nib can be loaded automatically if they match. I use ViewControllerName_iPad or ViewControllerName_iPhone and create the view required depending on device idiom.
The code to load from the xib file is not in the viewDidLoad method of the view controller itself.
You should usually find it in the application delegate's didFinishLaunchingWithOptions: method, or in the info.plist file, under the NSMainNibFile entry.
Try changing the initWithNibNameOrNil method to just init.
If that doesn't work, also override the loadView method by uncommenting it and setting your view there.
Here's an example:
- (id)init
{
self = [super init];
if (self) {
// Custom initialization
}
return self;
}
- (void)loadView {
[super loadView];
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,460)];
myView.backgroundColor = [UIColor redColor];
self.view = myView;
[myView release];
}
Perhaps I'm barking up the wrong tree here, but is the UIViewController loading its view from a XIB because the XIB that loads the UIViewController itself has a "NIB Name" set in the view controller's settings?
e.g. load up MainWindow.xib, see your view controller. Select it, then look in the view controller settings over on the right (4th tab). One of the settings is "NIB Name". Just make that blank to stop the view controller loading its view from that XIB/NIB.
I'm writing an app that will run on iOS3.0 and up.
The app has a custom UIViewController (say), which I'm instantiating from a .xib file. It's view comprises a single UILabel, which I've correctly declared and synthesized etc. in my custom UIViewController header and implementation files.
I'd like to set the text of this UILabel dynamically, and for it to be shown to the user by the time my UIViewController appears. For sake of argument please assume the text setting method is expensive.
The catch is on iOS3.0 with my UIViewController at least, -(id)initWithNibName:(NSString *) aNibNameOrNil bundle:(NSBundle *) aNibBundleOrNil returns before -(void)viewDidLoad does, but on my iOS4.0 device it's the other way around. The text label can therefore be nil when I don't want it to be.
Thus, I don't know where I can set the text in such a way as to keep both iOS3.0 and iOS4.0 happy.
Any advice here?
Can you please explain how your label comes out to be nil?
If you have taken an outlet for the label, then in either case, in the - (void)viewDidLoad method it cannot be nil, provided the label outlet is properly connected in the xib file.
If you have not taken the label outlet and doing it by code, then instantiate the label again in - (void)viewDidLoad method and set its text right over there and then add it to the view controller's view.
For more information - read the - (void)viewDidLoad and - (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle method in the UIViewController class reference
I have an iphone app that uses table view as the interface. Each time that the user taps on one of the table cells, I want to show another window to the user. However the user interface of the window that I push into the navigation controller are extremely similar. Therefore I decided to make a "generic nib file" to be used across all the subclasses of the file owner of this generic nib file.
However what I'm confused (and what's not working) is that I can't seem to be able to customise this generic nib file. I have this code at the initialisation of the files:
In the .h file:
#import <UIKit/UIKit.h>
#import "primeViewController.h"
#interface subclass1 : primeViewController { //subclassing from a generic view controller that owns a generic nib file
}
In the .m file:
#import "subclass1.h"
#implementation subclass1
- (id) initWithLabelAndButton {
if(self = [super initWithNibName:#"primeViewController" bundle:nil]) {
self.label.text = #"Title of the first subclass";
}
return self;
}
But then when I instantiate the class in the table view:
//somewhere in rootviewcontroller.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0:
{
checkPrime* checkPrimeViewController = [[checkPrime alloc] initWithLabelAndButton];
[self.navigationController pushViewController:checkPrimeViewController animated:YES];
[checkPrimeViewController release];
break;
}
default:
break;
}
}
So can anyone tell me what I'm doing wrong? Or am I wrong assuming that xcode allow me to use nib file multiple time across its subclasses? I don't see why I can't do it, but I can't figure out how...
When nib file is loaded, it creates view controller of exactly the same class that is written in .xib (UIViewController, or PrimeViewController, whatever else). What actually is saved in your .nib file?
You'll succeed if you will store UIView and all the corresponding objects in xib, and will load only them by using [[NSBundle mainBundle] loadNibNamed:#"YourNibFile" owner:class1ViewController options:nil], while view in nib is connected to the corresponding File owner outlet.
If you put all your views in one NIB, then when your application launches, it has to load the entire NIB into memory and construct all of the objects for all of the controls, and this takes a lot of time.
If you separate your views into separate NIBs, then your app will start up much faster, because only the NIB containing the initial view will be loaded. Then, when the view changes, you can load the NIB containing the new view. This will incur a minor hitch when opening a view for the first time.
Also if you're trying to optimize memory usage, you can unload the precious view when switching to a view
I'd suggest subclassing UITableViewController and add any methods, instance variables, and properties common to your different view here. Make your nib and have it have a reference to an instance of one of these objects.
Then, subclass your subclass to get customized functionality, like
GenericSubclassVC* checkPrimeViewController = [[SpecificSubclassVC alloc] initWithNibName:#"GenericNib" and Bundle:nil];
[self.navigationController pushViewController:checkPrimeViewController animated:YES];
You should stick to overriding the designated initializer. Put any custom initialization code into awakeFromNib or viewDidLoad methods, depending on if they need to modify UIView objects.
However, as others have mentioned, it's not terribly efficient or elegant. If all your ViewControllers are table view controllers and have the same data model to display, then there are other ways to get code reuse, like defining a datasource object.
I want to get objects values from a UIViewController.
I am using this
SecondViewController *newObject=[[SecondViewController alloc] init];
But newobject has textview, its value is zero. How can I access the textview?
you have to make the textview as a property to be able to access it outside.
Looks like the initialization process is incorrect:
If you use NIB file for you view then you should use
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
to initialize your controller. If you create your view programmatically, you must override controller's -loadView method. (see UIViewController reference for details)
Edit: Make sure that your textview is connected to some controller's outlet in the nib file.
When I need to load a view programmatically I do the following:
MyController* myController = [[MyController alloc] init];
[[NSBundle mainBundle] loadNibNamed:#"myNib" owner:myController options:nil];
// use my controller here, eg. push it in the nav controller
This works fine, but my controller's viewDidLoad is never called. So I resorted to manually calling it after the loadNibNamed call, but it doesn't seem correct. I was expecting the framework to call the viewDidLoad on my behalf. Is this the right way or I'm missing something?
I am new to Stack Overflow, but I discovered this question and discovered a couple other methods to load a nib file that ensure that the viewDidLoad method gets called automatically. Ed Marty's answer is correct, but it requires you to go outside the ViewController's code to load the nib file, these two examples I offer here allow you to keep your code inside the ViewController's implementation. Not necessarily better ways, just different. If you know of any drawbacks to doing it this way, please let me know your thoughts.
First, inside the initWithNibName:bundle: method of your UIViewController subclass, you can replace the call:
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
with something like this: self = [super initWithNibName:#"NameOfAlternateNibFile" bundle:nibBundleOrNil];
Or, you can accomplish what appears to do exactly the same thing by the following:
[[NSBundle mainBundle] loadNibNamed:#"NameOfAlternateNibFile" owner:self options:nil];
[self setView:self.view]; //this line ensures that the viewDidLoad method is called
The key is in understanding what is written in the comments above the function definition for $initWithNibName:bundle: in the UIViewController.h file (included at the bottom of my answer here, see italics).
The nice thing about doing this using either of these methods is that viewDidLoad gets called in either scenario.
Here are the directives listed in UIViewController.h:
The designated initializer. If you
subclass UIViewController, you must
call the super implementation of this
method, even if you aren't using a
NIB. (As a convenience, the default
init method will do this for you, and
specify nil for both of this methods
arguments.) In the specified NIB, the
File's Owner proxy should have its
class set to your view controller
subclass, with the view outlet
connected to the main view. If you
invoke this method with a nil nib
name, then this class' -loadView
method will attempt to load a NIB
whose name is the same as your view
controller's class. If no such NIB in
fact exists then you must either
call -setView: before -view is
invoked, or override the -loadView method to set up your views programatically.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
You should load view controllers with
MyController* myController = [[MyController alloc] initWithNibName:#"myNib" bundle:nil];
Make sure your MyController extends from UIViewController, and the View property is set properly in Interface Builder.
The views are loaded lazyly by UIViewController. If you use the accessor myController.view in your code, the view should be loaded and viewDidLoad be called.
I noticed the same thing. I think ViewDidLoad must be called by the view CONTROLLER. Since you don't have a view controllr in your nib, you have to call the viewdidload manually. I"m having to do the same thing.