I have a family of devices that are very similar and are controlled by an applet with three tabs. Within each view controller, I make use of the navigation controller to expand into the set up of each one of those features.
The first tab, the 'input' tab, especially is quite different between these three devices so when it is detected that I've switched between devices, I perform the following thing in my application delegate:
if ([self IsCrescendo])
{
//thirdViewController is really the crescendo'a input view - I need to rename that mess one day
crescendoInputView = [[ThirdViewController alloc] init : (UIViewController*) currentViewController];
crescendoInputView.title = [[viewControllers objectAtIndex:INPUT_TAB_INDEX] title];
crescendoInputView.tabBarItem = [[viewControllers objectAtIndex:INPUT_TAB_INDEX] tabBarItem];
[viewControllers replaceObjectAtIndex:INPUT_TAB_INDEX withObject:crescendoInputView];
[crescendoInputView release];
[self.tabBarController setViewControllers:viewControllers animated:FALSE];
}
if ([self IsSpirito3])
{ // similar to above using obviously a different view controller
}
if ([self IsSonata])
{ // similar to above using obviously a different view controller
}
Initially, this app just controlled one device so when I first created it, I set the three tabs up in the main window's XIB which works well. It defaults to the original device and the navigation bar is in tact and working.
Now that there are more devices to control, I figured to just use a replaceObjectAtIndex so swap a new view controller in but my Navigation bar disappears.
I'd very much appreciate any light you may be able to shed on this.
Thanks!
Okay, after lots more head scratching, the following fixed it:
I had initially used the main window's XIB to instantiate the three tabs.
This works fine if you're not doing a replaceObjectAtIndex. When I did do a ReplaceObjectAtIndex, it would lose the navigation bar.
Instead, if you instantiate the tabs programmatically, along with each having its own navigation controller, you can replace tabs with impunity and not lose features like the navigation bar.
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
// Input view (defaults to Crescendo)
UIViewController *viewController1 = [[[ThirdViewController alloc] initWithNibName:#"ThirdView" bundle:nil] autorelease];
UINavigationController *nav1 = [[UINavigationController alloc] initWithRootViewController:viewController1];
// Volume View
UIViewController *viewController2 = [[[SecondViewController alloc] initWithNibName:#"SecondView" bundle:nil] autorelease];
UINavigationController *nav2 = [[UINavigationController alloc] initWithRootViewController:viewController2];
// System view
UIViewController *viewController3 = [[[FirstViewController alloc] initWithNibName:#"FirstView" bundle:nil] autorelease];
UINavigationController *nav3 = [[UINavigationController alloc] initWithRootViewController:viewController3];
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:nav1, nav2, nav3, nil];
.
.
}
Not sure why it does not work when you set it up from a XIB. I could swear I had it working on a previous version so maybe something changed and apple forgot to tell us about it.
I like this approach better anyway. It isn't the first time a 'wizard like' programming tool has bit me so maybe this will save someone out there a little time.
Related
I am relatively new to iOS, hence I apologize for any inconsistency in my question. I need help with the following issue with an app I'm trying to build. My issue is this: The app i am working has a navigation based functionality with a tableview(daily filled by user) and a detailed tableview listing the inputs of the user, but this is just one functionality of the app.
I want to have a main tab based view where one of the tabs(each tab representing a functionality) points to this module.
I wanted to ask for steps and changes i need to make to for example app delegate or rootviewcontroller(I can post the code if it helps better) to make is so that the app starts with a mutli-tabbed bar view where one tab refers to view linked to the rootviewontroller of the navigation-based app.
For summary: Need a main tab bar view where one tab points to the rootviewcontroller highlighted in the screenshot(link below)
If helpful here is a relevant function code i have in app delegate :
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
RootViewController *rootViewController = (RootViewController *)[[navigationController viewControllers] objectAtIndex:0];
rootViewController.managedObjectContext = self.managedObjectContext;
//Next TWO LINES FOR COLOR BACKGROUND
[[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundColor:[UIColor redColor]];
}
PS:Here is the screenshot for the storyboard: where i would like to have one tab refer to the view(highlighted in the screenshot) which is linked class rootviewcontroller.m/h
The screenshot: http://i.stack.imgur.com/G9AXI.png
edit: The actual question can be seen as: How and what do i need to do to have a tabbarviewcontroller which i would add with storyboard become my rootviewcontroller instead of the navigationcontroller(highlighted in black in the screenshot: http://i.stack.imgur.com/G9AXI.png).
My current rootviewcontroller.m manages anything related to the tableview of the current navigationviewcontroller, do i need to change that also?.
I apologize for excessiv details, I am really new to iOS dev.
From this one http://i.stack.imgur.com/suLBm.png I tried to embedd in tab barviewcontrol only with storyboard to this one http://i.stack.imgur.com/TZxLo.png I tried to embedd in a tab controller just by story but i get an error :'NSInvalidArgumentException', reason: '-[UIViewController setManagedObjectContext:]: unrecognized selector sent to instance 0x8184e30'
classes related to this are(especially rootviewcontroller.m which is a navigationcontroller for now:
AppDelegate.{h,m}
Configures the Core Data stack and the first view controllers.
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
RootViewController *rootViewController = (RootViewController *)[[navigationController viewControllers] objectAtIndex:0];
rootViewController.managedObjectContext = self.managedObjectContext;
}
RootViewController.{h,m}
Manages a table view for listing all values entered. Provides controls for adding and removing these values.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
DetailViewController.{h,m}
Manages a detail display for display details of each entered value.
My initial guess is that i need to change the rootviewcontroller appdidfinishlaunching.
Any suggestions ?
In fact now you have:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UITabBarController *tabController = (UITabBarController *)self.window.rootViewController;
RootViewController *rootViewController = (RootViewController *)[[[[tabController viewControllers] objectAtIndex:0] viewControllers] objectAtIndex:0];
rootViewController.managedObjectContext = self.managedObjectContext;
}
So you actually need a UITabBarViewController in the Storyboard and you can point to the UINavigationController if you want the ability to push other controllers.
You don't need other UINavigationControllers as I saw in your screenshot, as long as the rootviewcontroller is an UINavigationController.
You can add the UINavigationController as first of the tabs and then you can go and fill the other tabs with the viewcontrollers that you need displayed.
SO basically you need to create UITabBarController as rootviewcontroller.
Let me know if I understood your question correctly.
Here is an example of UITabBarController :
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
//Here you set your controller
UIViewController* centerController = [[UIViewController alloc]init];
UINavigationController *navCenter = [[[UINavigationController alloc] initWithRootViewController:centerController] autorelease];
UITabBarController *tabBarController = [[[UITabBarController alloc] init] autorelease];
tabBarController.viewControllers = [NSArray arrayWithObjects:navCenter,nil];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.window.rootViewController = tabBarController;
return YES;
}
Let me know if it worked.
You should have something like this :
Hi Fellow iOS Developers, I am a newbie developing a project with 5 tab Views and on the first and second tabs I have slide out menus using Container views from example code by Michael Frederick on his GitHub page Project Link: https://github.com/mikefrederick/MFSideMenu. He is using a nib (.xib) files though I am using Storyboard to achieve the same and struck with defining the container and child views. can kindly some one advice how to modify the below code to accommodate in my storyboard.
the original code in the AppDelegate.m is
- (DemoViewController *)demoController {
return [[DemoViewController alloc] initWithNibName:#"DemoViewController" bundle:nil];
}
- (UINavigationController *)navigationController {
return [[UINavigationController alloc]
initWithRootViewController:[self demoController]];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:[NSArray arrayWithObjects:[self navigationController],
[self navigationController], nil]];
SideMenuViewController *leftSideMenuController = [[SideMenuViewController alloc] init];
SideMenuViewController *rightSideMenuController = [[SideMenuViewController alloc] init];
MFSideMenuContainerViewController *container = [MFSideMenuContainerViewController
containerWithCenterViewController:tabBarController
leftMenuViewController:leftSideMenuController
rightMenuViewController:rightSideMenuController];
self.window.rootViewController = container;
[self.window makeKeyAndVisible];
return YES;
}
#end
how to modify the code to accommodate the container parent view and child views ?
where should i instantiate the code for the parent and child of the 2nd tab view ? in AppDelegate or the View Controller ?
If any other Details are required leave a comment please. Any Help Will be greatly appreciated. thanks in Advance.
I don't know if you still need this, but i had the exactly same problem today, too. What you need to do is:
remove the both methods over your app Delegate
put this in your app Delegate:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"YOUR_STORYBOARD" bundle:[NSBundle mainBundle]];
MFSideMenuContainerViewController *container = (MFSideMenuContainerViewController *)self.window.rootViewController;
UIViewController *leftSideMenuViewController = [storyboard instantiateViewControllerWithIdentifier:#"THE_IDENTITY_OF_YOUR_SIDEMENU"];
UITabBarController *centerViewController = [storyboard instantiateViewControllerWithIdentifier:#"IDENTITY_OF_YOUR_TABBARCONTROLLER"];
[container setCenterViewController:centerViewController];
[container setLeftMenuViewController:leftSideMenuViewController]; //for the right Side, its the same way...
[container setPanMode:MFSideMenuPanModeNone]; //remove this line, if you need the pan mode
return YES;
In your Storyboard you have to put a ViewController as a subclass from "MFSideMenuContainerViewController". Mark this View as the "Initial View Controller" in the Attribute Inspector. Now use a Segue from your new Initial View Controller and let it "push" to your TabBarController. To avoid a Warning rename the Segue.
After you have done this, you can add a UIBarButtonItem to every View, you like to add the SideMenu. In the Action Method of this UIBarButtomItem you only need to do this:
[self.menuContainerViewController toggleLeftSideMenuCompletion:^{}];
finally make sure you have a UIViewController or a UITableViewController, that is your "SideMenu" and set the right Storyboard ID.
if you are still need help, comment this...
and sorry for my english :)
You can use https://github.com/ozcanakbulut/VoovilSideMenu. It's easy to embed in a tabBarController. It uses Storyboard and Arc.
I've had a good look at the Apple docs as well as similar Stack Overflow questions, but I am stuck on why my navigationController is null when using tab bars. I am trying to build most of the app from code, and am not using XIBs to insert a navigationController.
While debugging I have greatly simplified my app, down to two tabs. One tab holds a tableview and when a row is touched I'm expecting a detail page (from a XIB) to appear. Should be pretty simple. I am finding the value of self.navigationController is NULL when attempting to push the detail view, and of course it is then not working. I took the tab bar our completely and it works fine from a single view (the tableview). In this instance self.navigationController has a value.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// With Tab Bars
self.tabBarController = [[UITabBarController alloc] init];
ViewController *vc1 = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
vc1.tabBarItem.title = #"Words";
vc1.tabBarItem.image = [UIImage imageNamed:#"tab_feed.png"];
TextTableViewController *vc2 = [[TextTableViewController alloc] init];
vc2.tabBarItem.title = #"Text";
vc2.tabBarItem.image = [UIImage imageNamed:#"tab_live.png"];
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc1];
NSArray* controllers = [NSArray arrayWithObjects:vc2, navController, nil];
tabBarController.viewControllers = controllers;
tabBarController.delegate = self;
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
window.rootViewController = self.tabBarController;
[window makeKeyAndVisible];
return YES;
}
From TextTableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
TextViewController *detailViewController = [[TextViewController alloc] initWithNibName:#"TextViewController" bundle:nil];
Text *text = [[Text alloc] init];
text = [textArray objectAtIndex:indexPath.row];
detailViewController.TextID = text.textID;
NSLog(#"Nav Controller: %#",self.navigationController);
[self.navigationController pushViewController:detailViewController animated:YES];
NSLog(#"pushed");
}
I've also got two questions related to this problem.
(1) What is the purpose of this line. It doesn't appear to make a difference if it is in or out, and is absent from the Apple example.
tabBarController.delegate = self;
(2) When creating an array of tabs, one of the views is made the navigationController. Does it matter which tab it is or should this be a different view altogether not related to any tab and not visible. Is this where the problem lies?
In answer to question (1) about the tab bar controller's delegate, see the UITabBarControllerDelegate protocol reference. For the basic functionality of a tab bar controller, you don't need to bother with a delegate.
But let's say you want to do something special--for instance, save a document or reset an interface element to a default value--when the user changes tabs. You could make one of your classes, perhaps your app delegate or another controller class, conform to the UITabBarControllerDelegate protocol and implement tabBarController:didSelectViewController:
In your "answer" you asked if each tab will need its own UINavigation controller. That is absolutely correct. Basically, each tab is a completely independent hierarchy, so you need a separate UINavigation controller in each tab that requires one.
This should also imply the answer to your question (2) in the original post. You need to add the nav controller to the specific tab(s) that needs it.
OK I found it. The UINavigationController needs to be contained within the appropriate tab of the UITabBarController. So by making this coding change (below), a new UINavigationController is embedded in the tab with the tableview.
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc2];
NSArray* controllers = [NSArray arrayWithObjects:vc1, navController, nil];
Which then begs the question: what if you need multiple examples of this - do you create a new UINavigationController for each tab that has a need for one, and mark each one as a rootViewController?
I have a problem I can't figure out, I have made an application which uses UIsplitview inside a tab bar. I have been implementing the different tabs however now when I am working on the first tab - the UIsplitview is not aligned in landscape mode. Do you guys have any suggestions - if I start it in portrait and go to landscape, then there's no problem at all.
Update:
I dont do any init with frames anywhere, and I have checked the sizes etc. in IB. The following shows how I add the uisplitview controller in the app delegate. It has been done this way because I wanted a splitview in a tabbar controller. When i have added the spilview I just set the master and detail view in IB. A bit of mystery.
if (index == 2) {
detailViewController = [[DetailUserCreatorViewController alloc] initWithNibName:#"DetailUserCreatorView" bundle:nil];
userContent=[[UserContentForPopViewController alloc]init];
userContent.userDetails=detailViewController;
detailViewController.delegate=userContent;
//rootViewController.navigationItem.title = #"List";
UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:userContent] autorelease];
splitViewController = [[UISplitViewController alloc] init];
splitViewController.tabBarItem = controller.tabBarItem;
splitViewController.viewControllers = [NSArray arrayWithObjects:nav, detailViewController, nil];
splitViewController.delegate = detailViewController;
[controllers replaceObjectAtIndex:index withObject:splitViewController];
}
Update: I tried to set the selected tab in application didfinishlaunch in the app delegate - self.tabBarController.selectedIndex = 0; and this made the tab start at the correct placement. However it does not seem to be a proper solution.
Some pointers...splitViewController needs to be added as a subview of window:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
return YES;
}
The following code is incorrect. You should not assign a viewController to a delegate.
splitViewController.delegate = detailViewController;
You will also not require this line of code:
[controllers replaceObjectAtIndex:index withObject:splitViewController];
The following line handles that part of assigning delegates.
splitViewController.viewControllers = [NSArray arrayWithObjects:nav, detailViewController, nil];
Also, if you can upload your code, I'll try to correct it and post back the reason and corrected code...
I have developed a program for iPad programmatically. I have a SplitViewController, RootViewController and DetailViewController.
I am able to get the UITableViewController as a popover.
How to get a UILabel been displayed on DetailViewController on click of each index of the UITableViewController programmatically?
Thanks in advance..
The template that Apple provides for you in XCode does exactly what you are looking for.
Firstly, I realise the original poster has probably long since moved on from needing an answer to this question. Since I recently needed to search for a similar question, did not find a suitable question/answer, I decided to start digging for myself, and came up with an answer that may be useful for someone else.
As mentioned by #Wes, the template provided in XCode for "Master-Detail Application" does create useful jumping off point to create a universal app with a view controller:
XCode 4.2 New Project Dialog
be sure to also choose Universal..
What I did find however, which was not inherently obvious, is that whilst when using the app as an iPhone app, you will get a call to method setDetailItem: in the detailViewController (which will in turn call configureView), you won't automatically get this happening in the iPad version of the app. this is because the _detailViewController ivar (mapped to by detailViewController property) in masterViewController is nil by default.
This is because in the iPhone version, when you select a detail item, the default code checks to see if this iVar is nil, and autocreates one for you. in the iPad version however, the [appDelegate application: didFinishLaunchingWithOptions:] has already created a single detailViewController for you, along with a masterViewController. to get this working in my code, what I did was add the following lines in the [appDelegate application: didFinishLaunchingWithOptions:] method, as follows:
masterViewController.detailViewController = detailViewController;
[masterViewController tableView:masterViewController.tableView
didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
The second line automatically selects the first detail item for you on app start. This is safe in the default demo code, because it is set up to handle 1 detail. if you are going to change that in your code (eg by creating the detail items dynamically from some other source) you will of course need to ensure there is a valid detail item there to select at position 0.
The method (after i added these 2 lines) looks like this now:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:#"MasterViewController_iPhone" bundle:nil] autorelease];
self.navigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease];
self.window.rootViewController = self.navigationController;
} else {
MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:#"MasterViewController_iPad" bundle:nil] autorelease];
UINavigationController *masterNavigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease];
DetailViewController *detailViewController = [[[DetailViewController alloc] initWithNibName:#"DetailViewController_iPad" bundle:nil] autorelease];
UINavigationController *detailNavigationController = [[[UINavigationController alloc] initWithRootViewController:detailViewController] autorelease];
masterViewController.detailViewController = detailViewController;
[masterViewController tableView:masterViewController.tableView
didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
self.splitViewController = [[[UISplitViewController alloc] init] autorelease];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil];
self.window.rootViewController = self.splitViewController;
}
[self.window makeKeyAndVisible];
return YES;
}