subclassing UITableView so it doesn't need a xib - iphone

I made a XIB that only consisted of a UITableView in IB. It was brought to my attention that it is possible to subclass UITableViewController and do away with the xib entirely.
My question is, how do you do this?
So far the only thing I have changed is my .h to be...
#interface MyView : UITableViewController
and removed my XIB. Obviously I get an error that states
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Could not load NIB in bundle: 'NSBundle (loaded)' with <path>
name 'MyView''
so my question is, what else is left in order to accomplish this subclassing correctly?

There are two ways to set up your view controller in iOS.
With a .xib file: You instantiate the view controller with -initWithNibName:bundle:, passing the name of your .xib file as the first argument, and (unless you're doing some advanced stuff) nil as the second argument . The OS will look for your .xib file and unserialize it into a bunch of objects and attach them to your view controller. Then the OS calls your view controller subclass's -viewDidLoad: method, where you finish setting things up.
Programmatically: You instantiate the view controller with -init, or for a table view controller, initWithStyle:. The OS then calls your subclass's -loadView method, where you manually instantiate your view hierarchy.
It sounds like you just need to instantiate it with -initWithStyle: instead of -initWithNibName:bundle:.

If you had a ViewController without a .xib you could just create a tableview in the view did load or something.
UITableView *table = [[[UITableView alloc] initWithStyle:UITableViewStylePlain] autorelease];
table.dataSource = self;
table.delegate = self;
table.frame = CGRectMake(0, 10, self.view.frame.size.width, self.view.frame.size.height);
[self.view addSubview:table];
And then you just need to implement the proper delegate and data source methods like you would in a UITableViewController.

Related

Issues Using .xib With Storyboard

Hey I'm trying to open a view controller that is designed using a xib interface file. I am using the following lines of code to generate the controller from within a view controller created as a storyboard component.
YourViewController *viewController [[YourViewControlleralloc] initWithNibName:#"ViewControllerName" bundle:nil];
[self presentViewController:viewController animated:YES completion:nil];
I have never set up a view controller with a .xib before nor have I ever linked to one via storyboard so anything could be wrong. Here is the error I am getting when I try to present the view.
2013-05-17 13:06:45.120 Coffee[8991:907] * Terminating app due to
uncaught exception 'NSInternalInconsistencyException', reason:
'-[UIViewController _loadViewFromNibNamed:bundle:] loaded the
"PeekPagedScrollViewController" nib but the view outlet was not set.'
Set the File's Owner in the .xib to the class of your view controller (perhaps it is called PeekPagedScrollViewController).
Then connect its view outlet to the main view in the .xib file.
It was connected properly I had just forgotten to declare an initWithNibName method and therefore it crashed when I called it from storyboard. Silly mistake but thanks for all the great feedback #matt
Set the Storyboard view controller custom class to your class name (the one your xib is also referencing), then in that same class, simply override initWithCoder with:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [[super initWithCoder:aDecoder] initWithNibName:#"YourCustomViewControllerXibsName" bundle:nil];
if( self ) {
}
return self;
}
This will enable you to design layout and segue transitions in Storyboard, but design your layout in a standalone nib file.
Storyboard will handle instantiation, but when initWithCoder: is called, initWithNibName: will be the method that instantiates self via the xib.

UIView presented with Black screen, fix?

I call a View to be presented with the following code:
#import "infoView.h"
...
infoView *viewInfo = [[infoView alloc] initWithNibName:nil bundle:nil];
viewInfo.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:viewInfo animated:YES];
But when it is presented in run-time the view that is loaded turns out black.
Currently I am using storyboard, but I need to use this code, for it is a lot more efficient in my case, because I am dealing with multiple views!
It works fine if I connect it via StoryBoard.
I should be seeing 2 labels, 1 UITextView, and 2 UIButton.
The view was created using StoryBoard, when the .m and .h files for the view where created I did not add a .xib for it. And also it is linked through the "Custom Class" section in StoryBoard.
Thanks, hope someone can help!
It's generally pretty bad form to mock people who are taking the time and effort to help you.
Naming is important it makes your code easier to work with and allows other people to use it. Not following the conventions for the language you are working in is dangerous and means that your code is not compatible with other developers as things are interpreted differently.
If you look at the docuemntation for UIViewController you'll see this note in the initWithNibName:bundle: method description
If your app uses a storyboard to define a view controller and its associated views, your app never initializes objects of that class directly. Instead, view controllers are either instantiated by the storyboard—either automatically by iOS when a segue is triggered or programmatically when your app calls the storyboard object’s instantiateViewControllerWithIdentifier: method. When instantiating a view controller from a storyboard, iOS initializes the new view controller by calling its initWithCoder: method instead. iOS automatically sets the nibName property to a nib file stored inside the storyboard.
Therefore you are instantiating your controller wrong, the storyboard should be instantiating it. Which is done like this (naming corrected)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:[NSBundle bundleForClass:[self class]]];
InfoViewController *infoViewController = [storyboard instantiateViewControllerWithIdentifier:#"InfoViewController"];
infoViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self infoViewController animated:YES];
Side note
infoView is a bad name for the class not only because you didn't start with a capital but also because it's completely deceiving. Anyone reading this would assume that InfoView is a subclass of UIView not UIViewController.

I can make UINavigationController load only at 2nd level, not at Root View Controller

I tried looking for a similar problem but I could not find a similar question.
I am loading a UINavigationController in a UIView which is not (as in most examples) the MainWindow.
I created one new .xib called DocumentsViewController which is subclass of UIView (it has the related .m and .h files). And I created a DocumentsRootViewController.xib, which is a subclass of UITableViewController, which is supposed to be the UINavigationController's RootViewController.
I moved to DocumentsViewController and added a UINavigationController object in Interface Builder. Then I went to code, and added it as in IBOutlet, and connected it to the object.
In the ViewDidLoad, I execute the following lines:
DocumentsRootViewController *rootViewController = [[[DocumentsRootViewController alloc] init] autorelease];
rootViewController.title = #"Documents";
[navigationControllerDocuments initWithRootViewController:rootViewController];
[self.view addSubview:navigationControllerDocuments.view];
It shows the table as intended, but it shows a "Back" button to the "Root View Controller" (as in picture below).
Why? Shouldn't it already know that the rootviewcontroller has been set?
Thank you in advance to the ones that clarify this doubt
Giovanni
When you add the UINavigationController via the Nib it actually creates an instance of a UINavigationController inside the nib file with a default RootViewController set (of type UIViewController) and with a default title of RootViewController.
When you load the nib, this object is being created as part of loading the nib (i.e when you initialise DocumentsViewController) - so the navigationControllerDocuments outlet is already initialised as a UINavigationController with the default ViewController already set on it.
What I think is happening is when you call 'initWithRootViewController' - you are calling this on an already initialised object - so it is running the initialisation code again - pushing the second view controller (the DocumentRootViewController) onto the stack, but the default one that was created in the nib is already there.
What you should probably do is forget about creating one in the nib and initialise the whole thing programatically.
i.e. where you do:
[navigationControllerDocuments initWithRootViewController:rootViewController];
I suggest that you do an alloc and init instead:
[[navigationControllerDocuments alloc] initWithRootViewController:rootViewController];
Since you are doing this you really don't need to have the navigation controller added to the nib so if this works you should remove it from the nib since you are replacing it with this one in code.

UIViewController and XIB question

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.

UIView subviews don't responde to delegates

I have following code in viewDidLoad
myViewController = [[UIViewController alloc] initWithNibName:#"myView" bundle:nil] ;
self.myView = myViewController.view;
[self.view addSubview:myView];
This code loads view from myView.nib (I have corresponding view controller m and h files of course). myView view has UITextField and other controls. So far so good - I can see all my controls on the screen.
The problem however is that even though I set a delegate for UITextField to be File's Owner (which is myView) and I implement UITextFieldDelegate in myView.m, delegate methods are never fired!
This does NOT happen if I add UITextField to the original view (created by the XCode as default template). Why is this happening?
The reason I need to useSubview because in actual code will layer view to the UIScrollView so I can pan and zoom.
THanks!
My first question to your question is, "is there a reason to use UIViewController to load your NIB file?".
It appears that you're "throwing away" the UIViewController and merely using it for its ability to load a NIB file. I'd stop that first, as that may help you debug this issue. Use:
self.myView = (UIView*)[NSBundle loadNibNamed:#"myView"];
The second part I'd question is that you say "File's Owner" -- because you're using a separate UIViewController, you may have it set to call back to your UIViewController, not the code that you mean to callback to. Maybe you could tell us a little about the setup of the XIB file?