I have a UISplitViewController from which I need to load a UIViewController. I know I can do this as a modal, but that's not what I'm after. I need for the UIViewController to load like a normal VC occupying the full screen and when the DONE button is touched for it to return to the UISplitViewController. Thus far, when I've tried to do this it loads into one half of the UISplitViewController instead of replacing it.
So, my question is, "Is this even possible?" and if so, how? Currently I'm using the modal (PageSheet), which would be fine in Portrait orientation but just looks bad in Landscape.
Currently, my Split View is set up in the App Delegate via a tab bar as follows:
AdminMasterViewController *adminMasterVC = [[AdminMasterViewController alloc] init];
adminMasterNav.viewControllers = [NSArray arrayWithObjects:adminMasterVC, nil];
AdminDetailViewController *adminDetailVC = [[AdminDetailViewController alloc] init];
adminDetailNav.viewControllers = [NSArray arrayWithObjects:adminDetailVC, nil];
UISplitViewController *adminSplitVC = [[UISplitViewController alloc] init];
adminSplitVC.viewControllers = [NSArray arrayWithObjects: adminMasterNav, adminDetailNav, nil];
adminSplitVC.delegate = self;
adminSplitVC.title = #"Admin";
Then loaded with the tab bar.
I don't know why I just can't grasp the relationships of VCs to one another, apparently a missing chunk of brain matter.
why you don't want to use modal? you can have a full screen, just set up modal presentation style
YourUIViewController *viewController = (YourUIViewController*)[yourStoryboard instantiateViewControllerWithIdentifier:#"YourUIViewController"];
[viewController setModalPresentationStyle:UIModalPresentationFullScreen];
/*some additional logic if needed*/
[yourSplitViewController presentViewController:viewController animated:NO completion:nil];
Related
I have recently removed the tab bar from my app in favor of a "slide out" styled menu which I have written myself. It appears to be working perfectly, except that the one split-view controller I use in the app does not work correctly on the iPad in portrait orientation (landscape is just fine). The problem I'm seeing is the detail VC ONLY is loaded into the portrait view, where both VCs load correctly in landscape view. This is on iOS7.
So, here is the code I'm using. This code has been moved from the AppDelegate (previously) to the root VC. There were some changes but relatively minor. It did work fine with the tab bar, but using this new slide out menu (i.e., a table view) I have this one problem. I'm only including the portions of the code that I think would be relevant.
AdminMasterViewController *adminMasterVC = [[AdminMasterViewController alloc] init];
UINavigationController *adminMasterNav = [[UINavigationController alloc] init];
adminMasterNav.viewControllers = [NSArray arrayWithObjects:adminMasterVC, nil];
adminMasterNav.view.frame = CGRectMake(0,0,[Utility screenWidth],[Utility screenHeight]);
AdminDetailViewController *adminDetailVC = [[AdminDetailViewController alloc] init];
UINavigationController *adminDetailNav = [[UINavigationController alloc] init];
adminDetailNav.viewControllers = [NSArray arrayWithObjects:adminDetailVC, nil];
adminDetailNav.view.frame = CGRectMake(0,0,[Utility screenWidth],[Utility screenHeight]);
UISplitViewController *adminSplitVC = [[UISplitViewController alloc] init];
adminSplitVC.viewControllers = [NSArray arrayWithObjects: adminMasterNav, adminDetailNav, nil];
adminSplitVC.delegate = self;
adminSplitVC.title = #"Admin";
adminSplitVC.view.frame = CGRectMake(0,0,[Utility screenWidth],[Utility screenHeight]);
vcArray = [NSArray arrayWithObjects:homeVC, adminSplitVC, expressiveNav, receptiveNav, typerNav, nil];
(The last line builds the array of all the VCs in the project; the code which creates these VCs has been omitted).
After the VC has been selected it is presented as a child VC as follows:
UIViewController *vc;
if ([selected isEqualToString:#"Home"])
{
vc = [vcArray objectAtIndex:VCHome];
}
else if ([selected isEqualToString:#"Administrator"])
{
vc = [vcArray objectAtIndex:VCAdmin];
}
... (others listed here)
[self.view addSubview:vc.view];
[self addChildViewController:vc];
[vc didMoveToParentViewController:self];
From what I have read there may be some issue with NOT using the tab bar, but the articles are confusing because a lot of them are dated. I will appreciate any input or suggestions on this problem as I've read everything I can find and don't really know where to go from here. TIA.
After a couple of days of reading and trying different things, an hours after posting the question I solved this. Just in case anyone else hits this same problem, here's the solution:
I subclassed the UISplitViewController and made it its own delegate.
Then, this delegate method solved it:
- (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{
return NO;
}
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.
A is a subclass of TabbarViewController
A *a = [[A alloc] init];
B *b = [[B alloc] init];
C *C = [[C alloc] init];
NSArray *viewControllers = [NSArray arrayWithObjects:b,c, nil];
[a setViewControllers:viewControllers];
UINavigationController *nv =[[UINavigationController alloc] initWithRootViewController:a];
nv.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:nv animated:YES completion:nil];
And in A.m: I find the a.navigationController.navigationBar is nil
I dont know why?
Normally, you should create several NavigationController objects for each of TabBarController's tabs.
When you create a navigation interface, you need to decide how you intend to use a navigation interface. Because it imposes an overarching organization on your data, you should only use it in these specific ways:
Install it directly as a window’s root view controller.
Install it as the view controller of a tab in a tab bar interface.
Install it as one of the two root view controllers in a split view interface. (iPad only)
Present it modally from another view controller.
Display it from a popover. (iPad only)
If you still want 'TabBarController inside NavigationController' functionality, please, read this or this SO questions to find a proper solution.
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 to show quick look of a document in the detailView of a splitview based app. In the master view I have a UITableView with the list of all the files in the Document folder of my app. I'm trying to use the QLPreviewController in the DetailViewController, in this way:
QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = ...;
[[self navigationController] pushViewController:previewController animated:YES];
[previewController release];
I'm able to see the preview of the document, but I don't longer see the toolbar at the top of the detail view, and in portrait mode I'm stuck forever, because I'm not able to see the file list in the master view. I also tried to make DetailViewController subclass of QLPreviewController, but without success.
I think instead of pushing the previewController into the [self navigationController], what I believe you need to do instead, from what you described, is to set the previewController as the detail view of your UISplitViewController.
This can be done like so
[splitViewController setViewControllers:[NSArray arrayWithObjects:masterViewController, previewController, nil]];
If you want to have the navigation bar for for the previewController to appear, you can wrap the previewController with a UINavigationController before setting it as the detail view in UISplitViewController like so:
UINavigationController *wrapperNavigationController = [[[UINavigationController alloc] initWithRootViewController:previewController] autorelease];
[splitViewController setViewControllers:[NSArray arrayWithObjects:masterViewController, wrapperNavigationController, nil]];
Cheers
Try
[self presentModalViewController:preview animated:YES];
instead of
[[self navigationController] pushViewController:previewController animated:YES];