How to enable landscape orientation in a Tab Bar + NavigationControllers app? - iphone

I'm programmatically creating a tab bar based app. In the application:didFinishLaunchingWithOptions (app delegate) method I've put:
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
SearchViewController *search_vc = [[[SearchViewController alloc] init] autorelease];
SearchBookmarksViewController *search_bookmarks_vc = [[[SearchBookmarksViewController alloc] init] autorelease];
ItemsBookmarksViewController *items_bookmarks_vc = [[[ItemsBookmarksViewController alloc] init] autorelease];
AboutUsViewController *aboutus_vc = [[[AboutUsViewController alloc] init] autorelease];
UINavigationController *search_nav = [[[UINavigationController alloc] initWithRootViewController:search_vc] autorelease];
UINavigationController *search_bookmarks_nav = [[[UINavigationController alloc] initWithRootViewController:search_bookmarks_vc] autorelease];
UINavigationController *items_bookmarks_nav = [[[UINavigationController alloc] initWithRootViewController:items_bookmarks_vc] autorelease];
UINavigationController *aboutus_nav = [[[UINavigationController alloc] initWithRootViewController:aboutus_vc] autorelease];
NSArray vc_stack = [NSArray arrayWithObjects:search_nav,search_bookmarks_nav,items_bookmarks_nav,aboutus_vc,nil];
tabBarController.viewControllers = vc_stack;
[self.window addSubview:tabBarController.view];
SearchViewController is basically a search form showing results in a UItableView with rows which when selected show details about the chosen row. User can also press a "View Photos" button that push a view controller with a UIImageView to show some photos.
Now when working on the photo view I had to handle landscape orientation which seems not working easily in tab bar applications. Can you help about what to do exactly to allow this photo view controllers to handle landscape orientation ?
I've been told to make shouldAutorotateToInterfaceOrientation method to return YES in every view controller thus making any view to allow landscape orientation OR to subclass TabBarController and this last solution could cause my app to be rejected by Apple. So I'm a bit confuse about what to do exactly...
Thx in advance for your help!
Stephane

Its very simple you just have to make :-
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
for your each and every ViewController.

I've used both of the options you mention.
make shouldAutorotateToInterfaceOrientation method to return YES in every instanced/subclassed view controller: For this to work you need to make sure that every single view controller has its shouldAutorotateToInterfaceOrientation method to return YES. One option is to CMD+Shift+F to search in the whole project for ": UIViewController", this will show you a list of all the headers files that are inheriting from UIViewController, so set shouldAutorotateToInterfaceOrientation to YES in every implementation file corresponding to this headers. (This because sometimes you may've included a third party code that has some classes that inherits from UIViewController and that you omit to set shouldAutorotateToInterfaceOrientation to YES there)
subclass TabBarController: Apple documentation says that "This class is not intended for subclassing." so it would be better to avoid this option, I have used and it works, but you will always has the possibility of been rejected by Apple. (Maybe not the first time, but on any upgrade)
Anyway, I think Apple doesn't encourage tabbar applications to be displayed in landscape mode because tab bar took a lot of the space on the screen. So it would be better to consider the architecture of your application to see if a tabbarcontroller is really needed or create your own tabbarcontroller control like Twitter app on iPhone. This way you can reduce the height of the tab bar when in lanscape.

In addition to enabling rotation in each of the subview controllers (which is enabled by default if you're developing in ios5), if you want to take over the whole screen do something like:
UIInterfaceOrientation toOrientation = self.interfaceOrientation;
if (self.tabBarController.view.subviews.count >= 2 )
{
UIView *transView = [self.tabBarController.view.subviews objectAtIndex:0];
UIView *tabBar = [self.tabBarController.view.subviews objectAtIndex:1];
if(toOrientation == UIInterfaceOrientationLandscapeLeft || toOrientation == UIInterfaceOrientationLandscapeRight) {
transView.frame = CGRectMake(0, 0, 568, 320);
self.portraitView.hidden = YES;
self.landscapeView.hidden = NO;
tabBar.hidden = TRUE;
self.wantsFullScreenLayout = YES;
}
else
{
//Designing to accomodate the largest possible screen on an iPhone. The structs and springs should be setup so that the view will remain consistent from portrait to landscape and back to portrait.
transView.frame = CGRectMake(0, 0, 320, 568);
tabBar.hidden = FALSE;
self.portraitView.hidden = NO;
self.landscapeView.hidden = YES;
self.wantsFullScreenLayout = NO;
}
}`
UPDATE:
https://stackoverflow.com/a/13523577/1807026

Related

Parent view is not displayed after the low memory warning is received during MFMailCompose modal is open, canceled and draft saved/deleted

I have the following structure on my iPhone app
AppDelegate / UITabBarController / 5 UINavigationControllers(My tabs) / UIViewController(like rootViewController for each UINavigationController)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
HomeViewController *homeViewController = [[HomeViewController alloc] init];
GoalsTableViewController *goalsTableViewController = [[GoalsTableViewController alloc] init];
HistoryViewController *historyViewController = [[HistoryViewController alloc] init];
SettingsViewController *settingsViewController = [[SettingsViewController alloc] init];
InfoViewController *infoViewController = [[InfoViewController alloc] init];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.delegate = self;
self.navBarActivity = [[UINavigationController alloc] initWithRootViewController:homeViewController];
self.navBarSettings = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
self.navBarHistory = [[UINavigationController alloc] initWithRootViewController:historyViewController];
self.navBarGoals = [[UINavigationController alloc] initWithRootViewController:goalsTableViewController];
self.navBarAbout = [[UINavigationController alloc] initWithRootViewController:infoViewController];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:self.navBarActivity, self.navBarGoals, self.navBarHistory,self.navBarSettings, self.navBarAbout, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
In some UIViewControllers I implemented a MFMailComposeViewController in order to send emails.
I experimented a weird issue (reproduced on simulator and real devices iOS 5.0 and 5.1)...
Using an iPhone Simulator (only iOS 5.0 or 5.1), if I simulate a low memory warning while a MFMailComposerViewController modal is open on the screen and then tap on Cancel and then tap on Delete|Save draft, when the modal is dismissed the parent view seems not visible (blanked view).
The life cycle seems work fine due, if I follow the same steps but after simulate a low memory warning I send the email from MFMailComposeViewController modal, when modal is dismissed, my parent view looks fine.
Any suggestions how to prevent my parent view from being unloaded on memory warning?
Edit1
I figured out what is happening, after unloading and comeback to the view and entering the last view within viewdidload(life cycle), the tabBar is not inserting navigation view. I check the subviews of tabBar:
UITransitionView
==><UIViewControllerWrapperView>
==> empty
<UITabBar>
I reintegrated the view of navigationBar by adding as subview in viewdidload:
UIView *tabBarControllerWrapperView = [[[self.tabBarController.view.subviews objectAtIndex:0] subviews] objectAtIndex:0];
// tabBar UIViewControllerWrapperView has not views
if([tabBarControllerWrapperView.subviews count] == 0)
{
// add navigationbar view
[tabBarControllerWrapperView addSubview:self.navigationController.view];
}
There is no better way to fix it, any thoughts?
After a memory warning is possible that on the non visible controllers is called viewDidUnload (iOS <= 5 ).In your case the view of the controller that presented the modal mailcomposer was probably unloaded.
The idea behind viewDidUnload is that you save the data you need to restore the view once viewDidLoad is called again. What you have to keep in mind is that your viewDidLoad can be called multiple times.
On iOS6 viewDidUnload is not called anymore, so this logic must be moved to didReceiveMemoryWarning

How to display NavigationController-background within a UIPopoverController on iPhone

How can i display the background of the NavigationController (including title/buttons) in my UIPopovercontroller on the iPhone?
At the moment it looks like that:
In my PopoverView-ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBar.hidden = NO;
self.navigationController.navigationBarHidden = NO;
self.navigationItem.title = #"self.navigationItem.title";
}
When calling Popover:
InfoView *iv = [[InfoView alloc] initWithNibName:#"InfoView" bundle:nil];
UINavigationController *uc = [[UINavigationController alloc] initWithRootViewController:iv];
self.pop = [[UIPopoverController alloc] initWithContentViewController:uc];
[self.pop setDelegate:self];
[self.pop presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:true];
It should look like this:
At first UINavigationController doesn't have own background, it shows background of content view controller. If you can show popover with navigation controller successfully, then check your InfoView. Does it's view has some background?
Second, I tried to execute your code multiple times in different configurations on iPhone, but always have received error
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[UIPopoverController
initWithContentViewController:] called when not running under
UIUserInterfaceIdiomPad.
And that was not surprise for me. Even if you could make it work, then Apple may reject your application.
2.1 Apps that crash will be rejected - Just test your code on several devices and see result.
your code
InfoView *iv = [[InfoView alloc] initWithNibName:#"InfoView" bundle:nil];
UINavigationController *uc = [[UINavigationController alloc] initWithRootViewController:iv];
self.pop = [[UIPopoverController alloc] initWithContentViewController:uc];
[self.pop setDelegate:self];
[self.pop presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:true];
is perfect just hide the default navigation controller and add a navigationBar in the XIB of InfoView.xib and write the cancelButtonAction there to perform the dismissal of the view or the detail of the tableview selection. It Works fine, Please check the image i have done the same way, and believe me it works fine.
Add this to your code before the line [self.pop presentPopoverFromBarButtonItem:se..:
uc.modalPresentationStyle = UIModalPresentationCurrentContext;
See the documentation for the modalPresentationStyle property.
I think you'll need to paste in more of the code for your InfoView controller, as I suspect that's where the problem is.
Have you tried setting up your background view as a class?
[pop setPopoverBackgroundViewClass:bgView];

iPhone rotating view in navigationViewController mode

hours ago I post a question on organizing portrait and landscape mode in iPhone and now I think I know how to do it using willRotateToInterfaceOrientation:duration.
The first screen is 'Map View' with one button that leads to 'Setting View'. The Map View does not support rotate but for the Setting View I made separate view for portrait and landscape and they swap accordingly when rotated.
, ,
As you can see when Setting button pressed SettingView is added on the view stack as usual. So basically I use three view controllers; Setting, SettingLandscape and SettingPortrait.
I still found problem in rotating view in iPhone when I use navigationViewController. Segmented control is not working. it crashes without error message. It used to working fine without rotation.- when I'm not using multiple view for rotation-.
rotateViewController.m
This is root view controller.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
-(IBAction) buttonPressed{
Setting *settingViewController = [[Setting alloc] initWithNibName:#"Setting" bundle:[NSBundle mainBundle]];
UINavigationController *navController1 = [[UINavigationController alloc] initWithRootViewController: settingViewController];
[self.navigationController presentModalViewController:navController1 animated:YES];
[settingViewController release];
[navController1 release];
}
Setting.m
This view controller does nothing but swap views when rotate and shows appropriate view between portrait and landscape.
In Setting.m, I swap view as follow;
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) {
NSLog(#"to Right");
SettingLandscape *setting_landscape = [[SettingLandscape alloc] initWithNibName:#"SettingLandscape" bundle:[NSBundle mainBundle]];
self.view = setting_landscape.view;
[setting_landscape release];
}
if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
NSLog(#"to Left");
SettingLandscape *setting_landscape = [[SettingLandscape alloc] initWithNibName:#"SettingLandscape" bundle:[NSBundle mainBundle]];
self.view = setting_landscape.view;
[setting_landscape release];
}
if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
NSLog(#"to Portrait");
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
if (toInterfaceOrientation==UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"to PortraitUpsideDown");
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
}
In viewWillAppear, Setting view controller also has ;
self.title = #"Shell ";
self.navigationController.navigationBarHidden = NO;
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStylePlain target:self action:#selector(Done)] autorelease];
and Done is
- (void) Done{
[self dismissModalViewControllerAnimated:YES];
}
SettingLandscape.m
This view stacked on when the view is rotated. This view controller has it's navigation bar.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.title = #"Setting Landscape";
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
in viewDidLoad;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"landscape:viewDidLoad");
//self.title = #"SettingLandscape";//not working!!
//self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Done1" style:UIBarButtonItemStylePlain target:self action:#selector(Done)] autorelease];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
stringflag4MapType = [[NSString alloc] initWithString:#"blah"];
stringflag4MapType = [defaults stringForKey:#"flag4MapType"];
if (![stringflag4MapType isEqualToString:#"Hybrid"] && ![stringflag4MapType isEqualToString:#"Standard"] && ![stringflag4MapType isEqualToString:#"Satellite"]) {
segmentedControl4MapType.selectedSegmentIndex = 0;
}else if ([self.stringflag4MapType isEqualToString:#"Standard"]) {
segmentedControl4MapType.selectedSegmentIndex = 0;
}else if ([self.stringflag4MapType isEqualToString:#"Satellite"]) {
segmentedControl4MapType.selectedSegmentIndex = 1;
}else if ([self.stringflag4MapType isEqualToString:#"Hybrid"]) {
segmentedControl4MapType.selectedSegmentIndex = 2;
}
and following call does not get invoked. strange. doesn't matter rotation works anyway.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
NSLog(#"to Portrait");// does not print out.
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
if (toInterfaceOrientation==UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"to PortraitUpsideDown");
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
}
ok now, as you can see from those snap shots there are two navigation bar and each has its bar button, Done and Item. The Done button came from Setting and the Item button from SettingPortrait or SettingLandscape. All button's selector is same, that leads back to map view. The button Done works fine, but the button Item crashes. I need a button on navigation bar after rotation that acts like back button . I guess once I did 'self.view = settingportrait.view;' the problem starts.
The reason why I need the Item button work is that the segmented control started crashing once I add code to support rotation. If I found reason how to make the Item button-that is inside rotation view- work I think I can make the segmented control work as well.
You can download the whole code at https://github.com/downloads/bicbac/rotation-test/rotate-1.zip
https://github.com/downloads/bicbac/rotation-test/rotate-1.zip
this sample code is amazing for me. It solve my problem of rotating view just by simple delegate method
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
my best attempt to answer question without looking at code (don't have time tonight :( )
When you present setting viewcontroller modally, your top viewcontroller is setting.
When rotation happens, you load setting_landscape or setting_portrait viewcontroller, but only retain the view inside setting_landscape|portrait. Thus, setting_landscape/portrait viewcontrollers are released. When device is rotated, it's probably "setting" viewcontroller receiving rotation message, not the "setting_landscape/portrait" viewcontroller because they are not pushed on to the viewcontroller stack.
So, when you click on item or segment control, it will call delegate, which is probably set to setting_landscape|portrait which is released already.
What is the message in console you get with crash?
My recommendation would be to build setting viewcontroller with segmented control, then use "willAnimateRotationToInterfaceOrientation:duration:" function to reposition the segmented control to the right position and frame. Just by returning YES to all orientation, rotation should be supported, doesn't it?
What was the reason for using two separate viewcontroller for landscape/portrait? (I do this sometimes, but rarely)
Edit* you need to use "willAnimateRotationToInterfaceOrientation" callback to animate the changes, not "willRotate..."

Custom UITabBarController

I want to customize the look and feel of the tab bar of a UITabBarController. I want to change the colors, the way the icon looks when they are selected, and also, most important of all, I want to reduce the size of the custom toolbar.
My approaches for this and the hurdles in it are:
A) The first solution which came to my mind was to create my own viewController which will act like a UITabBarController with buttons in the bottom and add this viewController to the window. Once when user taps a button at the bottom, swap the view in the viewable area with the new viewController's which corresponds to the button now tapped by user.
The problem with this strategy is: since I swap view's the corresponding viewControllers will not get these messages:
viewWillAppear
viewWillDisappear
viewDidAppear
viewDidDisappear
And all the rotation events
B) I could have used the accepted answer's approach in this thread:
Custom UITabBarController Problems with View Controllers and Views
But my tabBar's height is not the same as the default.
Due to the cited reasons above, I cannot use those approaches.
Having said this, I have no special requirement of More tab. I will be having only 5 tabs which will be displayed by the tab bar and hence the re-ordering of tab bar items is out of scope.
Awaiting suggestions and ideas.
I have never attempted something like this but as I see it, you are supposed to send those messages to your child view controllers manually.
It shouldn't be problem to send -viewWill/Did(Dis)Appear to the right controller at the appropriate moment. This is what UITabBarController does, too.
As for rotation events:
In shouldAutorotateToInterfaceOrientation:, forward this message to your child controllers and set your return value depending on their return values (UITabBarController only returns YES if all its child controllers return YES for the requested orientation).
Forward willRotateToInterfaceOrientation:duration:, didRotateFromInterfaceOrientation: and willAnimateRotationToInterfaceOrientation:duration: to the child controllers (at least to the currently visible one) when you receive them.
If you have set the autoresizing masks of your child controllers' views correctly, they you rotate and resize correctly when the system rotates your custom tab bar controller's view. (At least I think that's how it should work.)
Again, I'm not sure if this will work.
You can implement the following code for the creating the custom tab bar in that use to images using the CGRect make.further code is use for the creating the custom tab bar
-(void)applicationDidFinishLaunching:(UIApplication *)application {
// Add the tab bar controller's current view as a subview of the window
tabBarController.delegate = self;
tabBarController = [[UITabBarController alloc] init];
mainDashBoard = [[DashBoard alloc] initWithNibName:#"DashBoard" bundle:nil];
mainSearchView = [[SearchView alloc] initWithNibName:#"SearchView" bundle:nil];
mainMoreView = [[MoreView alloc] initWithNibName:#"MoreView" bundle:nil];
UINavigationController *nvCtr0 = [[[UINavigationController alloc] init] autorelease];
UINavigationController *nvCtr1 = [[[UINavigationController alloc] initWithRootViewController:mainDashBoard] autorelease];
UINavigationController *nvCtr2 = [[[UINavigationController alloc] initWithRootViewController:mainSearchView] autorelease];
UINavigationController *nvCtr3 = [[[UINavigationController alloc] initWithRootViewController:mainMoreView] autorelease];
UINavigationController *nvCtr4 = [[[UINavigationController alloc] init] autorelease];//[[[UINavigationController alloc] initWithRootViewController:nil] autorelease];
tabBarController.viewControllers = [NSArray arrayWithObjects:nvCtr0,nvCtr1,nvCtr2,nvCtr3,nvCtr4,nil];
nvCtr0.tabBarItem.enabled = NO;
nvCtr4.tabBarItem.enabled = NO;
[window tabBarController.view];
}

UISplitViewController strange behavior

Hi i have a splitViewController
mapViewController = [[MapViewController alloc] initWithManagedObjectContext:managedObjectContext startingRegion:startingRegion];
distanceViewController = [[DistanceTableViewController alloc] initWithManagedObjectContext:managedObjectContext];
distanceViewController.mapViewController = mapViewController;
setupViewController = [[SetupTableViewController alloc] initWithStyle:UITableViewStyleGrouped map:mapViewController.map];
setupViewController.positionSwitch.on = savePosition;
SearchTableViewController *searchViewController = [[SearchTableViewController alloc] initWithStyle:UITableViewStylePlain managedObjectContext:managedObjectContext];
searchViewController.mapViewController = mapViewController;
tabBarController = [[UITabBarController alloc] init];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
UINavigationController *mapNavigationController = [[[UINavigationController alloc] initWithRootViewController:mapViewController] autorelease];
UINavigationController *searchNavigationController = [[[UINavigationController alloc] initWithRootViewController:searchViewController] autorelease];
UINavigationController *distanceNavigationController = [[[UINavigationController alloc] initWithRootViewController:distanceViewController] autorelease];
UINavigationController *setupNavigationController = [[[UINavigationController alloc] initWithRootViewController:setupViewController] autorelease];
UISplitViewController* splitVC = [[UISplitViewController alloc] init];
splitVC.viewControllers = [NSArray arrayWithObjects:searchNavigationController, mapNavigationController, nil];
splitVC.title = #"iMetano";
splitVC.tabBarItem = [[[UITabBarItem alloc] initWithTitle:#"Mappa" image:[UIImage imageNamed:#"mapIcon2.png"] tag:0] autorelease];
NSArray *viewControllersArray = [NSArray arrayWithObjects: splitVC,setupNavigationController,nil];
[splitVC release];
tabBarController.viewControllers = viewControllersArray;
}
When i startup my app in portrait, all works fine.
When i startup my app in landscape this is the result
I see only the view of the first viewController SearchTableViewController with some pixel between the UINavigationController and the status bar
When i rotate in portrait and after i return in landscape i see both viewController's view, but the second have some pixel between the statusBar and the UINavigationControllor
I can't understand why.
apple says not to put a split view controller inside something else, like a tab bar controller
After looking at my code and IB time after time. This is the best that I could come up with. Not sure if is the best one but it works for me. Im loading a default detail view controller. If I load the controller directly in the viewDidLoad then the problem occur. If I load it from the selector the problem goes away. I hope this helps. I have this code in the RootViewController.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(loadController) withObject:nil afterDelay:0];
}
-(void)loadController{
UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
WebViewController *newDetailViewController = [[WebViewController alloc] initWithNibName:#"WebViewController" bundle:nil];
[newDetailViewController setTitle:#"Home"];
NewNavController <SubstitutableDetailViewController>*navController = [[NewNavController alloc] initWithRootViewController:newDetailViewController];
detailViewController = navController;
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailViewController, nil];
splitViewController.viewControllers = viewControllers;
}
I had this exact same problem when attempting the combination of tab bar, split view and navigation controllers. I noticed that the alignment gap is only present when the application first fires up and the first tab is auto-selected because it's the first tab in the tab bar controller's array of view controllers. After switching tabs and then coming back to the one with the misaligned nav controller in a split view, there was no alignment problem present. So, to replicate this behavior and get rid of the misalignment when the screen is first rendered I added:
[tabBarController setSelectedViewController:splitVC];
right after setting the view controller array on the tab bar controller. Works like a champ now.
I know this is an old question, but here's the hack I just used to get around this problem for anyone who has a navigation hierarchy like mine:
UITabBarController
Tab0->UINavigationController->MGSplitViewController _or_ UISplitViewController
Tab1->UINavigationController->SomeOtherViewController
Tab2->Etc...
Nothing I tried could get rid of that 20px gap that occurs only once, at bootup, if the device orientation is anything except UIInterfaceOrientationPortrait. The 20px gap is caused by the UINavigationBar for the split view's UINavigationController above having a non-zero origin.y value; most likely, you'll find it to be 20.
Also, I found that this is only a problem if the device is running iOS < 5.0.
I check for this issue in the view controller code of my MGSplitViewController (i.e. self = an MGSplitViewController):
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if(self.doIOS4OneTimeRotationHack == YES)
{
self.doIOS4OneTimeRotationHack = NO;
for(UINavigationController *navController in [self viewControllers])
{
if(navController.navigationBar.frame.origin.y != 0.0f)
{
[UIView animateWithDuration:0.01
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:
^(void)
{
navController.navigationBar.frame = CGRectMake(navController.navigationBar.frame.origin.x,0.0f, navController.navigationBar.frame.size.width,navController.navigationBar.frame.size.height);
}
completion:
^(BOOL finished)
{
//NSLog(#"Shifted navbar 0x%x up!",navController.navigationBar);
}];
}
}
}
}
With the animation set to finish in just 0.01 seconds, it happens so fast that you'll never even notice it as your bootup splash screen disappears and your MGSplitViewController view appears in its place. Maybe play around with it and make it instantaneous; I had to get it working and move onto my next task, so I didn't fool with it past that point.
I don't like resorting to hacks like this, but this was the only way I was able to get around this problem. ScottS' solution below sounded great, but unfortunately didn't work for me.