I am developing an iPad/Universal application, and I am facing the problem of handling a UINavigationController as the main detail view in a UISplitViewController. What I want to know is how to add the default side controller button to the detail controller when in landscape orientation.
Definition
The split view controller has been defined this way:
splitViewController = [[UISplitViewController alloc] init];
SideController *root = [[[SideController alloc] init] autorelease];
DetailController *detail = [[DetailController alloc] init];
InserisciDatiController *calcolo = [[[InserisciDatiController alloc]
initWithNibName:#"InserisciDatiNuovi"
bundle:[NSBundle mainBundle]]
autorelease];
UINavigationController *rootNav = [[[UINavigationController alloc] initWithRootViewController:root]autorelease];
UINavigationController *calcoloNav = [[[UINavigationController alloc] initWithRootViewController:calcolo] autorelease];
splitViewController.viewControllers = [NSArray arrayWithObjects:rootNav, calcoloNav, nil];
splitViewController.delegate = detail;
and later on I release all the releasable objects.
I am using the SideController as a sort of index for the detail controller. It has a table view, and clicking each row I update the main view controller of the detail. Each new controller is always an instance of UINavigationController.
Update the detail
myAppDelegate *delegate = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
UINavigationController *nav = (UINavigationController *)[delegate.splitViewController.viewControllers objectAtIndex:1];
[nav setViewControllers:[NSArray arrayWithObjects:controller, nil]];
I use this piece of code to update the detail controller, when the user taps a row in the side view controller.
Handle landscape orientation
I'd like to know how to handle the landscape orientation, when I want to add the default toolbar button to display the hidden side view controller. What I am not able to do is getting the toolbar of the detail navigation controller to add the button using this method:
- (void)splitViewController:(UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController:(UIPopoverController*)pc
And also, when I push a new controller to the navigation controller the default back button will appear in the toolbar. In this case how should I handle the button creation?
Update
Please, do not tell me that no-one has ever had this kind of problem! How do you develop iPad apps? Sob ...
I found a way that could be useful for someone, even though I do not really think this is a clean way of handling the navigation controller.
In the split view controller delegate I implemented the splitViewController:willHideViewController:withBarButtonItem:forPopoverController: method this way:
- (void)splitViewController:(UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController:(UIPopoverController*)pc
{
barButtonItem.title = NSLocalizedString(#"menu", nil);
myAppDelegate *app = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
UINavigationController *nav = [app.splitViewController.viewControllers objectAtIndex:1];
UIViewController *ctrl = [nav.viewControllers objectAtIndex:0];
if (!ctrl.navigationItem.backBarButtonItem && !ctrl.navigationItem.leftBarButtonItem) {
ctrl.navigationItem.leftBarButtonItem = barButtonItem;
}
}
Here I present the bar button item that will show the popover view representing the side view controller if and only if the navigation controller is not showing the back button or another custom left button item.
Hope this helps someone!
I believe that Slava Bushtruk of Alterplay has worked out what you're looking for and used it in his APSplitViewController library.
I think the reason you haven't gotten any good answers is that it's really hard to understand your problem, i.e., your question could be clearer.
So first let me see if I've understood the problem correctly. Is the problem that you don't know how to handle the case when you've pushed something on your navigation controller and is supposed to show both the back button and the split view button?
If so, the way we've solved that problem is that the split view button is only visible in the root of the navigation controller. The side controller is usually only relevant for the detail view's root view controller anyway.
If I've misunderstood your problem or my solution isn't applicable to your project, please let me know.
EDIT: So if you insist on doing this, here's a way to do it:
toolbar = [[UIToolbar alloc] initWithFrame:(UIInterfaceOrientationIsPortrait(interfaceOrientation)) ? CGRectMake(0, 0, 768, 44) : CGRectMake(0, 0, 703, 44)];
toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.navigationController.navigationBar addSubview:toolbar];
On viewWillDisappear:
[self setToolbarHidden:YES animated:animated];
and on viewWillAppear:
[self setToolbarHidden:NO animated:animated];
Now you have a toolbar that you can add anything to. Well, anything that can be added to a UIToolbar anyway, which is almost anything.
Related
I am trying to display a simple iPad popover which contains a navigationController with a tableView.
popover -> navigation controller -> view controller -> table view.
I do need the navigationController because on touching the cells I want to push another viewController (within that same popover).
Without the navigation controller, everything is fine.
But as soon as I put the viewController inside a navigationController, the tableView stops responding (didSelectRow doesn't get called). I suppose something is wrong with my delegates but I just can't work it out.
. The navigationController responds fine (I can hit a button I have in the top bar)
. The buttons that are IN the cells respond fine.
. If I touch down and hold on a cell it gets highlighted, but not selected.
UPDATE: I just found out that if I hold the cell down for at least a second, the delegate is called when I release it. any less than that and it is never called...???
Here is the code use:
ModalViewController* controllerWithTable = [[self storyboard] instantiateViewControllerWithIdentifier:identifier];
UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:controllerWithTable];
UIPopoverController* popover = [[UIPopoverController alloc] initWithContentViewController:navigationController];
popover.delegate = self;
[popover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
If you are using a popoverController to show a table view, you don't need to embed it in a navigation Controller, Instead make a different Viewcontroller with the table view and just shoe that inside a popover controller .I used it somewhat like this..
Make a global reference for UIPopoverController in your app delegate,
//in your Appdelegate.h file declare this
#property(strong, nonatomic) UIPopoverController *popOverForTableView;
Now In the controller which you want to show the PopOver view use this,
TableViewCOntroller* popoverContent = [[TableViewCOntroller alloc] init];
NSString *identifier=#"tableVC"
popoverContent =[[UIStoryboard storyboardWithName:#"Main"
bundle:nil]
instantiateViewControllerWithIdentifier:identifier];
popoverContent.preferredContentSize = CGSizeMake(330, 280);
AppDelegate *appDelegate=(AppDelegate *)[[UIApplication sharedApplication]delegate];
appDelegate. popOverForTableView = [[UIPopoverController alloc]initWithContentViewController:popoverContent];
appDelegate.popOverForTableView.delegate = self;
appDelegate.popOverForTableView.backgroundColor=[UIColor blackColor];
[appDelegate.popOverForTableView presentPopoverFromRect:self.date.frame inView:self.view permittedArrowDirections:(UIPopoverArrowDirectionUp) animated:YES];
You can dismiss it accordingly…,…see if this Helps..
Ok I found the answer there: https://stackoverflow.com/a/18159463/3562952
The tableView was not irresponsive, it was deceptively responding only after a 1-3 seconds hold down.
I had a tap responder on the parent view that was capturing the tap. I am now removing it when displaying the popover and putting back in on dismissal.
I was googling for the wrong symptoms :)
I need to have a tabbar kind of view in a view controller(say view controller 3) which is in navigation stack and will be pushed by another view controller (say view controller 2).
I added tool bar to view controller 3 with many buttons. But managing and switching between views seems difficult.
How can i add a tabbar controller in the middle of navigation stack.
please help me out.
You cnt create tabbar in the middle of view controllers, you can create tab bar structure using custom segment controller. check the link below for creating custom segment controller.
https://www.cocoacontrols.com/search?q=segment
You cannot add TabBar there - tabBar is only designed to be at the bottom.
You have to create your own ViewController and add it as subView for navigationViewController and implement whole switching methods.
There has been some discussion about this design in related questions.
The way I understand your question it's definitely possible though. I am pushing a UITabBarController onto a UIViewController that is embedded in a UINavigationController. I did everything in the interfacebuilder except for the push which I implemented programmatically.
So in ViewController.m I added an action to a button:
- (IBAction)searchForPlacesButtonPressed:(id)sender {
TabBarController *tc = [[self storyboard] instantiateViewControllerWithIdentifier:#"TabBarController"];
tc.title = #"Orte";
tc.tabBar.selectedImageTintColor = [UIColor colorWithRed:(28.0f/255.0f) green:(100.0f/255.0f) blue:(52.0f/255.0f) alpha:1.0f];
UIBarButtonItem * logo = [[UIBarButtonItem alloc] initWithCustomView:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon_xyz"]]];
tc.navigationItem.rightBarButtonItem = logo;
Places *d = [[Places alloc] init];
for (UIViewController *vc in tc.viewControllers) {
if ([vc isKindOfClass:[MapViewController class]]) {
((MapViewController *)vc).dataHandler = d;
((MapViewController *)vc).mapViewDelegate = d;
vc.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"Karte" image:[UIImage imageNamed:#"map"] tag:0];
}
else if ([vc isKindOfClass:[TableViewController class]]) {
((TableViewController *)vc).dataHandler = d;
vc.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"Liste" image:[UIImage imageNamed:#"eye"] tag:1];
}
}
[self.navigationController pushViewController:tc animated:YES];
}
Using the tabs at the bottom you can switch between viewing the places on a map and listed in a table.
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 :
In my iPad application, i have a main menu screen.. with various icons on it.
On tapping on an icon, it navigates me to Custom UISplitViewController. I did it with this code. Everything is working fine in SplitView.
Problem: I am facing issue to get back to my Main Menu screen on tapping a button in MASTER Viewcontroller's Navigation bar.
code for Custom UIsplitview:-
self.navigationController.navigationBarHidden = NO;
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
left = [[LeftViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *leftNav = [[UINavigationController alloc] initWithRootViewController:left];
right = [[RightViewController alloc] initWithNibName:#"RightViewController" bundle:nil];
UINavigationController *rightNav = [[UINavigationController alloc] initWithRootViewController:right];
left.right = right;
splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = [NSArray arrayWithObjects:leftNav,rightNav, nil];
splitViewController.delegate = right;
appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
UISplitViewController *cvc = (UISplitViewController *) splitViewController;
[appDelegate.window setRootViewController:cvc];
EDIT: Custom button code
UIButton *a1 = [UIButton buttonWithType:UIButtonTypeCustom];
[a1 setFrame:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)];
[a1 addTarget:self action:#selector(menu:) forControlEvents:UIControlEventTouchUpInside];
[a1 setImage:[UIImage imageNamed:#"icon.png"] forState:UIControlStateNormal];
UIBarButtonItem *random = [[UIBarButtonItem alloc] initWithCustomView:a1];
left.navigationItem.leftBarButtonItem = random;
- (void)menu {
[self.view removeFromSuperview];
ViewController *vc = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[appDelegate.window setRootViewController:vc];
[vc release];
}
This causes CRASH, with EXC_BAD_ACCESS message.
Pls guide me..
I assume you can see which line of your code the crash happens on by stepping through from a breakpoint?
I'm not a fan of your sw design here to be honest as I wouldn't use a UISplitViewController subclass. I'd be more likely to create a custom SplitViewController using UIViewController containment APIs and then use that in conjunction with a UINavigationController.
That said, if you must use a UISplitViewController subclass then I would suggest having all your code to add or remove the viewControllers from the window in the main appDelegate and then use notifications to tell the appDelegate when to add or remove the relevant viewControllers (switch the root). This way you reduce the potential to have multiple instances of the same view controllers hanging around leading memory issues. It's always tricky to try to get a viewController to remove itself from it's parent, and is best managed by parent or higher level entity (in this case the main app delegate).
I'm not sure this is a good design. What else do you do from your main menu screen bedsides going to the split view controller?
If you want to keep this design, you would do the same thing you did at the end of your posted code -- in the button's action method, create an instance of your main menu controller and set it as the window's root view controller.
After Edit:
I can see one problem that's sure to create a crash -- when you create your button, you have the action set as "menu:", but your method implementation is just "menu" with no colon or arguments. Remove the colon, and everything should work.
I don't think these 3 lines are causing trouble, but there's no need to do what you're doing:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
UISplitViewController *cvc = (UISplitViewController *) splitViewController;
[appDelegate.window setRootViewController:cvc];
You already have a property or ivar, splitViewController, there's no need to reassign it to cvc. Also there's no need to get the app delegate, you can get the window with self.view.window. So those 3 line can be changed to just this:
self.view.window.rootViewController = splitViewController;
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?