iPhone - Alternate Landscape View issue - iphone

I'm having the landscape mode issue and I can not find the way out. Basically, I'm having a tab bar application and in the first tab i have navigation controller. In this navigation controller, first view contains table with items and after clicking the item, detail view describing the item is pushed.
I need to implement landscape mode for both list and detail view, but for list view, i need to use different view controller for landscape mode (generally, something like cover flow). Detail view is just changing orientation and no need to use alternate view controller in this case.
I tried to achieve this behaviour by implementing modal view controller for list view controller, according to Alternate Views example by Apple. This works fine when I'm in list view (when I turn device into landscape mode, cover flow view controller is correctly presented). Problem comes when I'm showing detail view. When I change the device orientation, cover flow shows up again. What I expected is that cover flow will be presented only in case that list view is on the screen. It seems like modal view controller is always visible no matter what VC is currently on the stack of NC.
It seems to me that presenting modal VC as landscape view for particular VC is not working for multiple navigation levels.
I also tried to add landscape view as a subview into view controllers view. When using this solution, i have no problem with navigation levels, but issue here is that tab bar is not hidden in landscape mode. I need to hide tab bar for cover flow, which is achieved by presenting modal VC.
I will appreciate any help with this issue.
Great thanks!

In the detail view controller, you could set up a different view entirely using something like this (code from a recent project of mine):
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toOrientation
duration:(NSTimeInterval)duration
{
if ([graphView superview]) {
if (toOrientation == UIInterfaceOrientationPortrait ||
toOrientation == UIInterfaceOrientationPortraitUpsideDown) {
[graphView removeFromSuperview];
}
} else {
if (toOrientation == UIInterfaceOrientationLandscapeLeft ||
toOrientation == UIInterfaceOrientationLandscapeRight) {
[[self view] endEditing:YES];
[[self view] addSubview:graphView];
}
}
}
And now to hide the tabbar when you are in landscape (bit of a hack, but works):
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
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, 480, 320 );
tabBar.hidden = TRUE;
}
else
{
transView.frame = CGRectMake(0, 0, 320, 480);
tabBar.hidden = FALSE;
}
}
}
For this project, I added a view called "graphView" that I wanted to appear if and only if in landscape mode, and then I wanted to tabbar to be hidden. This sounds similar to what you're after, I think.
The only potential problem I foresee is that if you enter landscape mode before the detail view is pushed, things could get wonky. Therefore you may want to use these methods in the list view controller instead. This particular problem never arose for me, but it's something I thought about before I realized it was moot.

Related

Auto-Resizing a dynamic view when rotated

I have a tab bar application where everything is working fine. I have rotations of the device all working fine with the various Tab Bar View controllers.
Alas it was suggested that a couple of the View Controllers needed a help page. To this end I created a new ViewController that contains a UIWebView (where help can be built into an HTML file).
I create the new "HelpViewController" as follows:
mpHelpPage = [[HelpPageViewController alloc] init];
[mTabBarController.view addSubview: mpHelpPage.view];
[mpHelpPage retain];
mpHelpPage.view.alpha = 0.75f;
This brings up the help page no problems when I'm in portait mode. Unfortunately when I'm in landscape mode and I do the above code it adds the HelpViewController in Portrait (meaning it extends off the bottom of the screen).
As such when I alloc the ViewController above I need some way of telling the ViewController to rotate to the current device orientation.
I am, however, at a loss as to how to get it to do this. Any help would be much appreciated!
I handle this annoyance by putting an orientation check in viewWillAppear:, e.g.
if (self.interfaceOrientation == UIInterfaceOrientationPortrait ||
self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
// custom code or call willRotate
} else {
// custom code or call willRotate
}
You can also do this if you prefer
if (UIDeviceOrientationIsPortrait(self.interfaceOrientation)) {
// custom code or call willRotate
} else {
// custom code or call willRotate
}
you should either set the frame-property of your subview in willRotateToInterfaceOrientation:duration: of your ViewController
or you write your own View and set the frame-property in layoutSubviews of your View
The added Subview should handle the layout of its subviews.
Since you've added HelpViewController as a subview and no UIViewController controls it, it will not be resized. You can resize HelpViewController's view manually by detecting a change in the orientation in the shouldAutorotateToInterfaceOrientation: method of the current UIViewController. This method passes the current orientation as its argument, so just check which is the current orientation and set a frame accordingly as:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if((interfaceOrientation==UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation==UIInterfaceOrientationLandscapeRight))
mpHelpPage.view.frame = CGRectMake(0,0,480,300);
else
mpHelpPage.view.frame = CGRectMake(0,0,320,460);
return YES;
}
Or, Instead of adding HelpViewControlleras a subView, try [self.navigationController pushViewController:HelpViewController animated:YES];

UINavigationController and Alternate Landscape

In my application I use an Alternate Landscape Interface strategy (present your landscape view as a modal). I also use a navigation controller for transitioning and this causes the following problem: I dunno how to push/pop correctly from landscape orientation.
I came up with the following solution, but someone may know a better one. Suppose one has to deal with only two views. Let's call them AP, AL, BP, BL, where the second letter stands for orientation. We start with a navigation controller with AP inside. To go between AP and BP we just push/pop. To go from AP to AL we present a modal navigation controller with AL inside. To go between AL and BL we push/pop inside the second navigation controller. Now to go from BP to BL we pop w/o animation and present a modal navigation controller with BL sitting on top of AL. To go from BL to BP we dismiss the modal navigation controller and push BP w/o animation.
Seems to be a bit ugly, but not so bad. Can anyone think of something better?
Thanks in advance!
Is there some reason you need to present your landscape orientation as modal in a separate controller? When I have two entirely different views for my portrait and landscape orientations I fade between them as they stretch during the rotation.
This allows for vastly different content in both orientations, a nice transition between them, and shared code under one controller.
Here is some code. Our UIViewController will switch between portraitView and landscapeView when we change orientation.
portraitView and landscapeView are both children of the UIViewController's view. The hierarchy looks as follows:
UIViewController
|
- view
|
|- portraitView
|
|- landscapeView
Both have their autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight to ensure that they stretch as the view controller rotates.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if( orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight )
{
[UIView animateWithDuration:duration
animations:^
{
//Fade the landscape view over the top of the
//portrait view as during rotation
landscapeView.alpha = 1.0f;
}
completion:^(BOOL finished)
{
//Hide the portrait view when landscape is fully
//visible
portraitView.alpha = 0.0f
}];
}
else
{
//Show the portrait view (underneath the landscape view)
portraitView.alpha = 1.0f;
[UIView animateWithDuration:duration
animations:^
{
//Fade out the landscape view to reveal the portrait view
landscapeView.alpha = 0.0f;
}];
}
}
Your controls and subviews will fade and deactivate along with the appropriate views, allowing you to have completely different content. I used this recently to fade between two different background images when changing orientation. The effect is very smooth.
You can now create your two view controllers, A and B which each manage two views as described above. You can then simply push the view controllers as normal and not have to worry about managing the UINavigationController's view controller stack during rotation.

Refresh ViewController on TabBarController

I have a tabBarController with three viewControllers on it.
When viewController 1 is selected and I make a 90 degrees I hide the tabBar and I have to addsubview the current view to the tabBarController, otherwise a blank space appears where the tabBar was.
If now I rotate the iPhone to the previously orientation (the vertical normal position) I removeFromSuperview the view, but no view is shown on the view controller, I suppose the original view (the one before the addsubview call) should be shown, in fact if I select the second viewController and later I go back to the viewController 1 the view appears perfectly.
I don´t understand why this happens, could you help me?
Update:
I think the problem is that I add a view over the tabbarcontroller (self.view addSubview:vista_AS.view]) I need this to make the tabbar not visible, and later, when I remove this view the tabbarcontroller loses in some way the viewcontroller 0 view reference. What I don´t understand is why when I change to viewcontroller 1 and then back to 0 the view is OK. Is there some way to reload viewcontroller 0 view??
Update 2:
Included author's code from a suggested edit to the answer
This is my code:
if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
self.tabBar.hidden = TRUE;
vista_AS = [delegate.tabBarController.viewControllers objectAtIndex:0];
vista_AS.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:vista_AS.view];
}
else {
if ( (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) )
{
[vista_AS.view removeFromSuperview];
self.tabBar.hidden = FALSE;
}
It appears your view controller 1 is being deallocated, either by yourself due to over releasing or by the system due to memory. Post some code showing how you attach and remove the view covering the tab bar. This may hold the answer.
When you add vista_AS as a subview of the tabBarController you change the parent view of vista_AS to its newest view parent, therefore breaking the link with tabBarController.
When you change iPhone's orientation, you remove vista_AS from its superview, but the link between the tabBarController and your view it is still broken. I believe that's why you can't see the view. A solution would probably go either by re-assigning vista_AS's parent to tabBarController.view or to do [tabBarController.view addSubview:vista_AS].

UINavigationController's navigation bar wont shrink when in Landscape mode

My navigation controller's navigation bar won't change the height when rotated to landscape.
see it stays at 44 pixels instead of 34 i think.
What do i do to fix this?
You have to add your navigation controller directly as a subview to your window, otherwise this doesn't work automatically. (It is not necessary to change the frame of your navigation bar manually.)
The -[application:didFinishLaunchingWithOptions:] method of your AppDelegate should contain something like
[window addSubview:self.yourNavController.view];
To get an example where this works automatically, you can also create a new navigation-based app in XCode and add an implementation for the shouldAutorotateToInterfaceOrientation: method of the RootViewController that always returns YES.
During the autoRotation method of your class, change the frame of your navBar like this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if((self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight))
{
self.navigationController.navigationBar.frame = CGRectMake(0,0,480,32);
}
else if((self.interfaceOrientation == UIInterfaceOrientationPortrait) || (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown))
{
self.navigationController.navigationBar.frame = CGRectMake(0,0,320,44);
}
else
{
assert(false);
}
}

iPhone screen rotates at random ?

I use a tabBar Controller as root controller. It has 4 tabs and each of its ViewControllers has
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
as well as the tabBarController itself.
But when I rotate the device (real or simulator), the screen turns at random! If it doesn't turn when I open the application it would have the same behavior until I quit the app.
I tried to add the 4 viewControllers one by one in IB to see if one was problematic, but I obtained the same issue. It only always turns when there is no tabs at all!
Please tell me if you have any ideas. Thanks!
You set every view controller to say that it responds to any possible orientation. Therefore, every view will attempt to rotate to every orientation.
Views don't really automatically rotate. You usually have to manage the placement of subview programmatically in all but the simplest views.
If you have no custom orientation code, you're probably seeing the views try to draw the portrait view in the landscape frame or vice versa. If you have autoresize subviews set your subviews will appear to scatter across the screen in a seemingly random pattern. The more you change orientation, the more random the placement becomes.
For complex views, I like to create separate viewController/view pairs for each orientation. Then I put the views in a nav controller. As the orientation changes, each view controller will push or pop the appropriate view controller for the coming orientation onto/off the stack. To the user, this looks like a single view is gracefully redrawing itself. (This is especially useful if you have non-standard UI elements that have to be manually rotated with transforms)
You have to subclass UITabBarController and implement shouldAutorotateToInterfaceOrientation:
Actually, I just want my first tab view controller to rotate. So I put this code in my custom tabBarController :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
if (self.selectedIndex == 0) {
return toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
}else {
return toInterfaceOrientation == UIInterfaceOrientationPortrait;
}
}
but I had the same problem. I use a custom orientation code for my first tab view controller when turning to landscape. Called with the following function in my custom tabBarcontroller:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
//rotation to Portrait
lastOrientation = toInterfaceOrientation;
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:NO];
[self.selectedViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
else if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
if (!UIInterfaceOrientationIsLandscape(lastOrientation)) {
//rotation to Landscape
[self.selectedViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
lastOrientation = toInterfaceOrientation;
}
}
I found that if you set the selected tab programmatically the tabViewController rotates erratically.