How to create CustomTabBarController using XIB? - ios

I am using iOS 5.1 and I need to create a Custom TabBar for my app. When app will be launched, the control will be pushed to the Custom TabbBar Controller class. Here is what I am doing. I have created class derived from UITabBarController and I used XIB also. In the XIB, I can see the UIView Objetc containing the Tab Bar and when I am adding more Tabs in that tabBar, it is not reflecting when I run the App and I can see only One Black TabBar without any title and color. I remember earlier we use the MainWindow.xib to add the TabBarController and for each TabBar Item we add the Navigation Controller so that every tab has its own Navigation controller but I am not getting how it can be done if I have used the XIB derived from UiTabBarController Class.Please let me know if I am unclear at any point. I will give more clarification. All I want to know why the changes on the TabBar of the XIB are not reflecting in the app?
The lower image showing the XIB and App so you can see that I have added the Four Tabs in the UITabBar but in the app there is only one Black Bar.

From the Apple Documentation about UITabBarController:
This class is not intended for subclassing. Instead, you use instances of it as-is to present an interface that allows the user to choose between different modes of operation
You can't subclass it. If the original UITabBarController doesn't have the features you are looking for, your only choice is to create a new custom tab bar controller without subclassing the original one.
EDIT: not sure if you want to do this. Are you simply trying to load an UITabBarController from a nib? If so, subclassing is not necessary. It would be better to use storyboards ;-) but you can do it even with nib.
You have to simply do this:
1- add an empty xib to the project and call it TabBar (only xib. No source files)
2- put an UITabBarController inside that nib. No other components, only that UITabBarController
3- put this code in the AppDelegate application didFinishLaunchingWithOptions method:
// this code should be already in that method if you have used the empty application model
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// this is the new code to add. You are programmatically loading an instance of UITabBarController from the nib named TabBar.xib
UITabBarController *tb = (UITabBarController *)[[[NSBundle mainBundle]loadNibNamed:#"TabBar" owner:self options:nil] objectAtIndex:0];
// here you are assigning the tb as the root viewcontroller
self.window.rootViewController = tb;
// this code is standard in this method
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
Then you can customize your UITabBarController and it should load correctly.
Take care: if you add other objects inside the nib, you must change the line
UITabBarController *tb = (UITabBarController *)[[[NSBundle mainBundle]loadNibNamed:#"TabBar" owner:self options:nil] objectAtIndex:0];
with a loop and some check to see if you are really loading the UITabBarController and not other components. Now I'm simply loading the first object in the nib, without taking care if it's a UITabBarController or an UIPotato ;-)

Related

UISplitViewController detailview changing result is gray area

I am trying to replace the detailview of a UISplitViewController for a quite a while now, but the solutions I found on the internet wasn't useful.
I am executing this:
DetailViewController* secondVc = [[DetailViewController alloc] init];
NSMutableArray* arr = [[NSMutableArray alloc] initWithArray:self.splitViewController.viewControllers];
[arr replaceObjectAtIndex:1 withObject:secondVc];
[self.splitViewController setViewControllers:arr];
DetailViewController is just a normal UIViewController (is this the problem?) I chose red as its background but I am seeing a completely gray area in the detail view after this code is executed.
What surprises me is that viewDidLoad and viewDidAppear functions are called for the DetailView class, but I can't see it on the screen. The self.view.frame is 0,0,768,1024 although all my settings are in landscape mode in storyboard.
I only want to use this in landscape mode, I don't need a generic solution.
What is the most basic way to change the detail view of a split view controller? I have looked at Apple's MultipleDetailViews but that felt like overkill since most of the code in it is about responding orientation changes, like hiding the master vc etc.
I suspect your problem is using alloc init to instantiate secondVC -- that would work if you made your controller's view in code, or in a xib with the name "DetailViewController". Since it appears that you're using a storyboard, then you should be using,
DetailViewController* secondVc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"]; // be sure to use this same identifier in the storyboard
In storyboard, select the view controller. On the right side, go to "Simulated Metrics" and pick "Detail" for "Size". As for the color, try setting it in viewDidLoad.

UISplitViewController is not being displayed correctly

I created and loaded a UISplitViewController in an existing ViewController by writing the following code in the viewDidLoad method:
LeftPanelViewController *leftPanel = [[LeftPanelViewController alloc] initWithNibName:#"LeftPanelViewController" bundle:nil];
FirstViewController *firstView = [[FirstViewController alloc] initWithNibName:#"FirstViewController_iPad" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:firstView];
UISplitViewController *splitController = [[UISplitViewController alloc] init];
splitController.viewControllers = [NSArray arrayWithObjects:leftPanel, self.navigationController, nil];
[self.view addSubview:splitController.view];
[self addChildViewController:splitController];
[splitController didMoveToParentViewController:self];
Everything is fine except for the fact that the splitController is not being drawn in the borders of the iPad, there's a space between the top of the screen and the top of the view. Even when I rotate the iPad the left panel is also having the same problem.
SplitViewController doesn't have a xib file, and when I change the added view for another that has, everything is correctly displayed.
Any ideas of what may cause this problem?
Notes:
Compiling and running the project in the simulator multiple times causes the SplitViewController to be displayed sometimes without any problems and others with spaces between any of the edges of the screen and the view. Running in the iPad causes always the same problem.
First of all.. why do you implement a container view controller? I guess you just want to present the splitViewController on its own, right? Than don't add the view yourself.
Instead correctly set it as your rootViewController on your window (preferably in applicationDidFinishLaunching).
self.window.rootViewController = splitViewController;
Container View Controller are not needed in standard cases. So you should never need to use the following methods:
addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:
Check the documentation of UIViewController.
If you really wanted to implement a Container View Controller, than you need to take care of the layout yourself. So you need to position / size the view of the other controller yourself. Depending on if you use AutoLayout or autoresizing, you need to set correct constraints/flags.

use different xib items with the same UIViewController class

I have created a .xib file containing a UITabBar with 5 UITabBarItems inside. I would like 4 of the 5 tabs to link to the same UIViewController class since they have the exact same interface (only the data differentiate their looks).
Therefore it makes sense for me to instantiate my UIViewController 4 times, once per tab bar item. And then link each one of the UITabBarItems of the .xib with one instance of my UIViewController.
But I cannot figure out a way to take a reference of my xib tab bar items in my UIViewController and send the setTabBarItem message. How could I achieve that ? I was trying somehow to pass the .xib tab bar items on init (overwriting the init) but I didn't manage to reference them. I instantiate the controllers in the AppDelegate after the self.window stuff.
(If I say something weird here, not making sense with the usual iOS programming conventions, please let me know)
Use UITabBarController for this , not sure what you exactly want to do with the same UIViewController but UITabbarController will definitely work;
UITabBarController *tabBarController = [[[UITabBarController alloc] init] autorelease];
ViewController *viewController1 = [[ViewController alloc]initWithNibName:#"ViewController1"];
ViewController2 *viewController2 = [[ViewController2 alloc]initWithNibName:#"ViewController2"];
tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1,viewController1,viewController1,viewController1,viewController2,nil];
self.window.rootViewController = tabBarController;

UITextView Delegate Issue in Stacked UIViewControllers

I want to stack a UIViewController Board on top of a UIViewController Menu in order to create a Facebook-like side menu. This menu should contain a UITextView.
So far I can drag the Board View side ways and the Menu appears underneath it. Great. But there's an issue with the UITextView inside the menu. When I click it the app crashes with a BAD_EXC... exception. It seems like an issue with the UITextView Delegate.
Here's how I currently set it.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//Board
board = [[BoardViewController alloc] init];
[self.window setRootViewController:board];
//Menu
MenuViewController* menu = [[MenuViewController alloc]init];
menu.textView.delegate = menu;
[self.window addSubview:menu.view];
//Window
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Note: when I set the textView delegate to board and implement the delegate methods there it works but that really seems to be the wrong place to implement the menu textview methods to me. The delegate of the menu should be in the menu class itself.
How to set the menu's textView delegate correctly to the menu class?
//Menu
MenuViewController* menu = [[MenuViewController alloc]init];
menu.textView.delegate = menu;
[self.window addSubview:menu.view];
And then, poof: the menu view controller isn't referenced again and is deallocated by ARC. The view is retained by the window so it will look like everything is ok until the text view tries to send its delegate message to an object which was long since deallocated. This is the cause of your EXC_BAD_ACCESS crash.
The simple, slack solution would be to define a property in your app delegate for the menu view controller.
#property (nonatomic, strong) MenuViewController * menu;
and then store the menu there
//MenuViewController* menu = [[MenuViewController alloc]init];
//becomes
self.menu = [[MenuViewController alloc]init];
The proper solution and the one I recommend is that you look up UIViewController containment and implement your own custom view controller container that looks after this special arrangement of view controllers.
Just to briefly outline: You would have a subclass of UIViewController with two properties, one for the board view controller and one for the menu view controller. It would have a scroll view and would be responsible for the sliding action and any communication that needed to be passed from the board to the menu and vis versa. This container would also be responsible for loading the board and the menu view controllers and inserting their views into the correct places in its own view. If the board view controller needs to be swapped out for another board then the container would be responsible for this also.
You would usually set that by dragging the delegate connection from the textField to the view controller in interface builder, or programmatically in viewDidLoad of the menu view controller. You're right in that your app delegate shouldn't have anything to do with this process.

UINavigationController stacks UITabBarControllers which stack other UINavigationControllers?

I read that it is bad to have such structure in an iOS application. But what if an application has a lot of UINavigationControllers and UITabBarControllers. But one UINavigationBar and one UITabBar are always displayed only? Other UINavigationBars and UITabBars are hidden.
EDITED
For example, in navigation-based application I call this code:
- (IBAction)openTabsController:(id)sender {
tabOneController *tabOneViewContr = [[[tabOneController alloc] initWithNibName:#"tabOneController" bundle:nil] autorelease];
UINavigationController *tabOneNavContr = [[UINavigationController alloc] initWithRootViewController:tabOneViewContr];
tabTwoController *tabTwoViewContr = [[[tabTwoController alloc] initWithNibName:#"tabTwoController" bundle:nil] autorelease];
UINavigationController *tabTwoNavContr = [[UINavigationController alloc] initWithRootViewController:tabTwoViewContr];
UITabBarController *tabContr = [[[UITabBarController alloc] init] autorelease];
tabContr.viewControllers = [NSArray arrayWithObjects:tabOneNavContr,tabTwoNavContr, nil];
sel.navigationController.navigationBar.hidden = YES;
[self.navigationController pushViewController:tabContr animated:YES];
}
After calling of this method I have two UINavigationControllers and one UITabBarController. At the same time I have one UINavigationBar and one UITabBar on a screen.
EDITED
Approximate scheme.
From The beginning we have an UINavigationController which allows to navigate between the views (circles). Then after pushing an UITabBar appears and allows to switch between the views. A rectangle with two little rects is a view with a UITabBar with 2 UITabBarItem s. When we presss any UITabBarItem another UIView appears. In this UIView we can press some button which calls another view with another UITabBar. Current UITabBar is visible after pushing if it is not hidden with another UITabBar.
is it more clear now?
The code above works almost perfect (except of some animations and not including Apple's limitations)
Gargo,
I'm not sure I understood your question but the apple documentation is clear. If you use - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated it says that viewController:
cannot be an instance of tab bar controller and it must not already be
on the navigation stack.
Since you do
[self.navigationController pushViewController:tabContr animated:YES];
you are pushing a tab bar controller instance within the navigation stack.
If you add the structure that you would achieve maybe I can help you to find another solution.
Hope that helps.
An app should only have one working tabBarController at any one time.
A tabBarController should also be the root view controller. Always. (If you need a login view or similar before the tabBarController, then remove the login view, create the tabBarController and then make that the root).
This is Apple' advice spoken to me personally by Apple engineers.
Remember, apps should be small applications that are quick and easy to use/navigate. If you feel the need for more than one tabBarController then your app design is likely very wrong from a UI/Usability perspective.

Resources