UINavigationController NIB requires File's Owner to have a view? - iphone

I'm having a problem setting a View Controller nib's default View Outlet in Interface Builder. Here's my setup:
I have a TabBar based application where I load a Navigation Controller as a modal view...
MyNavCtrlrSubClass *menu = [[MyNavCtrlrSubClass alloc]initWithNibName:#"MenuController" bundle:nil];
[tabBarController presentModalViewController:menu animated:anim];
MenuController itself is structured as follows:
MenuController.xib
File's Owner (MyNavCtrlrSubClass : UIViewController)
Navigation Controller (UINavigationController)
Navigation Bar (UINavigationBar)
Root View Controller (Nib Name is set to load AnotherViewController.nib)
Navigation Item -> (UINavigationItem)
This all works fine, except that when MyNavCtrlrSubClass is loaded, I get the following error:
Loaded the "MenuController" nib but the view outlet was not set
It's clear why this is happening - File's Owner doesn't have an outlet connection for its view. The question is what should I set as its view, and does something have to be set in the first place? The Navigation Bar is the only candidate in MenuController.xib, but doing this will just size the UINavigationBar itself to the fullscreen mode, so to speak.
I'm obviously missing something in IB, but what? MyNavCtrlrSubClass.m has no code itself, except an IBOutlet for the UINavigationController. Am I making a mistake trying to set this up entirely in IB? The idea is to keep the modal Navigation Controller in one nib, and all the views it loads in separate nibs, but since MenuController is just a container for the navigation and contains no views itself, I'm obviously designing it wrong. :)
If you're wondering why I'm not designing it some other way, it's because I'm trying to obey my (possibly mistaken) perception of how IB asks you to build an ideal hierarchy.
Any help would be greatly appreciated.

I think you might not be understanding how the File's Owner is meant to be used in a NIB file. I've written up a response describing the file's owner under another question.
When you invoke this line:
[[MyNavCtrlrSubClass alloc] initWithNibName:#"MenuController" bundle:nil]
You create an instance of MyNavCtrlrSubClass, and tell it to load #"MenuController". Then in the MenuController.xib file, there is a second unrelated UINavigationController with things inside of it. When MenuController.nib is loaded at runtime, that second navigation controller will be created.
A second problem is that telling a UINavigationController to load a NIB file isn't really sensible because navigation controllers create and manage their own views. You really want to create the root view controller, and tell that view controller to load a NIB file.
Try this:
MyRootViewController *rootController = [[[MyRootViewController alloc] initWithNibName:#"AnotherViewController" bundle:nil] autorelease];
MyNavCtrlrSubClass *menu = [[MyNavCtrlrSubClass alloc] initWithRootViewController:rootController];
Where your XIB File looks like this:
File's Owner (Class set to MyRootViewController, view outlet connected to subsequent UIView)
UIView
Subview A
Subview B
After you're confortable with how all of this is working, you might also consider instantiating the navigation controller and root view controller in one XIB file like you were starting to do in the code you posted.

The crux of this question is stated by Travis himself: "How would you load Navigation Controller nib, designing as much as possible in Interface Builder?" And also from the example, it looks like this means the UINavigationController and associated UIViewControllers.
With this interpretation, the answer is you cannot fully configure a UINavigationController and it's UIViewControllers in a single XIB. Yes it is intuitive to want to do this so you are not crazy.
When I say you cannot do this, I mean the most commonly used framework methods do not have a way to handle this. There is no [UINavigationController alloc] initWithMegaNibName. Yes you could stuff almost anything in a single XIB and write code to hydrate objects in special ways, but I don't think that's what you're looking for.
You could use two or more XIBs as Jon suggested, but then things are less self contained and so you have many folks who find it simpler to do part, or all, of the controllers in code.
Unfortunately there is not a 1:1 correspondance between Interface Builder capability and code like there is on other dev platforms. I generally prefer to be able let designers participate as much as possible in creating assets, but most of them I know do not code objective-c.

In IB, in the MenuController nib add a UIView and set that view as the outlet. You will need to set that view for a UIViewController. Here is a quick link to an Apple page showing a basic setup. Not sure if it will help you at your current stage though.

Related

Loading a UIViewController xib to ContainerView in storyboard

I have a "container" viewController, that hold a few container views using storyboard.
each of that container view has a link to an embedded viewcontroller's view.
Since im working on a big project, it turns out that the storyboard file is massive and contains a lot of "child" viecontrollers view layout. it will be a problem working on that file when a few people needs to be working simultaneously on it. and thats not good for me.
i would like to know if theres a way to load into each container view a viewcontroller's xib file, and still be using storyboard.
Meaning, creating .xib files for each viewcontroller instead of holding them in the storyboard itself, and link them to the container views in the storyboard.
thanks,
You can move your view controller layout into a separate xib. As you say, this is a very convenient way to share layouts across storyboards.
When setting up a container view in your storyboard, make sure you delete the provided view in the embedded ViewController. Set the custom class for the ViewController to your class name. Name the xib to match the class (e.g. FooViewController.xib so it can be found when FooViewController.m is loaded.)
If you don't delete the View in the storyboard view controller layout, you'll see that empty view instead. Your viewDidLoad method in your view controller won't be called, because the View is the default Storyboard View.
I'm using Xcode 6.1 for iOS 8 on a pre-iOS 7 project.
Yes, you can. A few observations:
All you need to do is to put in the sort of code that we used to use when using NIBs. In the case of containers, that means the typical container methods. If you've not done containment via code, see Creating Custom Container View Controllers in the View Controller Programming Guide. Bottom line, as you transition to a non-storyboard scene (or add a non-storyboard child view controller), just code it like you always used to in the NIB-based environment. You cannot represent this NIB-based scene in the storyboard. But you just get the controller like you always do with NIBs:
SecondViewController *controller = [[SecondViewController alloc] initWithNibName:nil bundle:nil];
You obviously lose many of the benefits of using storyboards (e.g. sizing the child scene on the basis of the container view in the parent scene), but you're no worse off than you were in a NIB environment. But in answer to your question as to whether you can "link them to the container views in the storyboard", you cannot represent this relationship in the storyboard itself, but rather you link them up programmatically.
If your separate teams are working in single-scene environments, you can use this NIB approach. You should also contemplate, though, just having multiple storyboards, one for each logical team. You'll still have to resort to code as you transition between storyboards, just like you do in this NIB approach, but if one of your teams has multiple scenes to deal with, they can enjoy storyboard benefits within their portion of the project. When you want to get to the first scene in the next storyboard, you can:
UIStoryboard *secondaryStoryboard = [UIStoryboard storyboardWithName:#"SecondStoryboard" bundle:nil];
SecondViewController *controller = [secondaryStoryboard instantiateInitialViewController];
If your child needs to transition to a new scene on your storyboard, I find it useful to add my own parentStoryboard property to my child controller, which it can then useful if you need to do something like instantiateViewControllerWithIdentifier. Obviously, if you're not transitioning to new controllers, you may not need to do that, but if you are, having a UIStoryboard property can be useful.

Storyboard done, Do I need to create .h and .m View Controller file for each View created?

I have created Storyboard with several views calling each other, now I need to create the code
I notice that XCode didn't created .h and .m controller files for each View from storyboard.
Should I create them manually?
Should I keep only one controller? (or few depending of separation of concerns on MVC)
Is there a pattern for developing this?
thanks
The usual approach is one view controller pr. screen full of content. You can imagine having one view controller for a tableview, with any sort of content, and then another view controller that presents that content in a new screen full of content if a row is pressed.
Normally when you have subviews inside of your view controllers, you wire them up in interfacebuilder. Then for instance if you want to populate a view that has a uiimageview and a uiactivityindicatorview inside it, you can control their behavior and how their populated from the view controllers code. You could also if you want something very generic and you feel that one view will probably take up a lot of code in your view controller, create a uiview subclass for it, and then set the class in interface builder.
Did this help? Please let me know if you need more clarification.
It's entirely up to you whether you have a ViewController for each view. If you have many views I would recommend it. Even if you have 2 or 3 views you probably still should. Things can get really confusing when each view has a different task but all have similar IBOutlets.
TLDR; Personally, I would say it was good practice to have a ViewController for each view if each view has a separate task.

Iphone using mainwindow.xib confusion

In a navigation based application, when I want to create and use other uiviews and uitableviews I need to create their controller and views. in an example I saw that I can simply create a new controller with .xib file, design it, and just call that xib file from my navigationcontroller.
In another example, some stuff was going on also in the mainwindow.xib and some new controllers and navigation items were added from the mainwindow.xib.
What is the difference between these methods? when and why I should need to open and edit the mainwindow.xib file to add a controller?
The mainwindow.xib is your UIWindow component which you can see as a representation of your iphone screen, it will always be there no matter what. In your examples when you are showing your view controller dirrctly that is because the controller is already a subview of your UIWindow which is the mainwindow.xib in the Interface Builder.
There really is no difference between the 2 methods, in the first one you are adding your controller as a subview progrmatically using:
[window addsubview:mynavcontroller]
And in the second one youbare doing it thru interface builder, you may use whichever method you feel more comfortable with.
You do not really need a controller to show a view, however they can be handy if you want to do any extra stuff such as rotating your view or loading certain data when the view is loading. That being said you could add your view as a subview of your window and it would still work.

What are the conventions for declaring a UITabBarController in mainwindow.xib?

I have a mainwindow.xib file with a UITabBarController as the base view controller of the app. So inside the UITabBarController I've added about 10 sub UIViewController objects as tabs. Most of them are just a UITableViewController subclass or a UINavigationController containing a UITableViewController subclass.
In this design, each UIViewController is fully loaded on app startup, including calling the viewDidLoad method of each view controller. Is there any way to get around that? Since the view controllers are just UITableViewControllers with no other outlets, it seems excessive to create a NIB for each tab (which I assume would allow the viewDidLoad to only get called when the user first switches to the tab? Or am I wrong on that?)
Anyway, my question mainly, is: how is it conventionally done? If you have 10 different view controllers on one UITabBarController, do you put them all in mainwindow.xib? If so, should each have its own NIB, and if not, where do you put them, and how do you add them to the tab bar?
What you want to do is to define the UIViewController views in a different xib file for each view - the reason they all get instantiated is that when the xib loads, all objects held in the xib load - and that means all your views and view controllers since you have defined them there.
In MainWindow.xib where you have the tab bar defined, you can still set within each tab the view controller type that will be called and also the XIB file to use for that type (create a new project with the "TabBar application" template and the second default view will be like this).
Then as you press tabs the view controllers will be instantiated from the different XIB files you have defined.
Note that this means if you are using IB to add buttons to the navigation bar, you have to do that back in the TabBar xib and not in the xib you use to define the view. You can still link actions to the view controller definition within the tab.
The way Apple suggests doing it for pure NIB files is how you say: Each sub-view in its own NIB file.
Instead of doing this, I would create the UITabBarController programmatically. That way you can define all your simple views in code, and still load complex views from NIB files.
Personally, I prefer creating as many of my views programatically as possible. The compiled code has a smaller footprint than the NIB files and I feel like I have more control. I mostly use Interface Builder to mock up applications.

How to use UISearchDisplayController from a controller within an UITabBar controller?

I have an UITabBar controller managing several controllers (using SDK 3.0). One of these is a tableView controller and I need to provide a search capability using the UISearchDisplayController. All of my code is based on Apple TableSearch example. However, when clicking on the tab, the tableView controller appears showing its related content, but no searchBar appears. I have checked the xib in IB to make sure that all of the outlets are properly set, but no matter what I try self.searchDisplayController is always nil and the search bar does not appear.
In practice I have replicated MainView.xib from the TableSearch example and set the file's owner class to the correct controller class for the tab. The outlets are sets as in the example MainView.xib. Am i missing any important step or doing something wrong?
Thank you in advance.
I had the same issue and happened to stumble upon this solution ...
If you have your table view controller (eg. UISearchDisplayController) nested in a Tab Bar or Navigation controller using Interface Builder, you need to set the "Nib Name" in the "Attributes Inspector" window.
The Nib name will be the one that holds the table view and has the controller (eg. UISearchDisplayController) as the File's Owner.
I too have this issue :( Is the search bar getting hidden behind the tableview?
#unforgiven,
did you try this...?
searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
[self.tableView setTableHeaderView: searchBar];
This manually creates a searchbar and it works. But I'm making some stupid mistake in IB that the SearchBar doesn't show up even though my connections are perfect. :-(
Do update this post if you get the answer...
Ok, I have found how to solve it. In my case, the problem was due to the fact that I was using a controller embedded within the UITabBarController as one of its managed tabs (i.e. as a child).
Removing the controller from the UITabBarController, then adding an UINavigationController to the UITabBarController instead, and finally putting my controller as a child of the UINavigationController solved completely the issue.
I do not understand why this is so (there is no related information in the documentation, as often happens); however, it now works like a charm.
With kind regards.
I had a similar issue
To solve I had to do one additional step to unforgivens answer
In my main nib
1) create a UITabController
2) Then I dragged out a UINavigational Controller into the tab controller
3) Then dragged out a UITableViewController into the NavigationalController as a child
4) Then changed (3) class to be my MyTableWithSearchBarViewController in the inspector - check if the nib name is correct and change this if necessary in the inspector as well
5) I then had to delete the tableView which is automatically created by IB in step (3). Only then would the search bar show correctly...
If in step 3 I dragged out a different controller onto the stage or left the tableView there it would only ever display the table and not the search bar
weird
Tomtrapeze has the right answer if your nib file contains the UITableViewController. But, if you're loading the UITableViewController in code -- such as pushing it on the stack of a UINavigationController -- the solution is a little different.
When initializing the UITableViewController or subclass, you need to use the -initWithNibName:bundle: form of initializer and specify the name of your nib file. Alternately, you could use the standard -initWithStyle: initializer and manually set the nibName property before the view is loaded.
If the nibName property is not set when the view gets loaded, UITableViewController will not use the normal UIViewController nib lookup logic. It will just load a standard UITableView instead.
I recently learned that I could load NIB files with [[MyViewController alloc] initWithNibName:nil bundle:nil]; when the NIB file has the same name as the class (i.e. MyViewController.xib). It turns out that initializing this way causes the 'UISearchBarandUISearchBarDisplayController` to not display. When I initialized the view controller by actually typing out the name of the class, my search bar showed up correctly. I initially thought it had something to do with how I was presenting the view controller but was glad that it wasn't.
PGWSearchViewController *searchVC = [[PGWSearchViewController alloc] initWithNibName:#"PGWSearchViewController" bundle:nil];
searchVC.modalPresentationStyle = UIModalPresentationFullScreen;
searchVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:searchVC animated:YES completion:^{ }];
This is using iOS SDK 5.1 and XCode 4.3.1.
Another possible fix that works for me is to init the UITableViewController with initWithNibName:bundle:
SearchEntryTableViewController* searchEntryTableViewController = [[SearchEntryTableViewController alloc]initWithNibName:#"SearchEntryTableViewController" bundle:nil];
To nest the UITableViewController in a UINavigationController before put it in a UITabBarController doesn't fix the issue for me...