Navigation Based App : only one landscape UIVIewController - iphone

still new to objective C and iOS i'm facing a new problem.
That's quite simple, i needed only one UIViewController in my app to be in landscape mode so i built my app this way :
One custom UINAvigationController : MyNavController set as "Is Initial View Controller" in my MainStoryBoard.storyboard. Then i add in this navController... :
- (NSUInteger)supportedInterfaceOrientations
{
return [[self topViewController] supportedInterfaceOrientations];
}
... to be able to control which UIVIewController childs of the custom nav controller can be in landscape orientation.
In the landscape UIVIewController i set :
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft ;
return UIInterfaceOrientationMaskPortrait;
}
so i can get this controller landscape or not.
In this landscape controller in my nib (landscape.xib) i set a welcome view in UIPortrait mode and behind this view i had my landscape view. So when the user pushed the controller he sees that he has to turn his iPhone screen. I listen to orientation changing using :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil];
In orientationChanged i hide the portrait view and then we can see the landscape view.
It works, but when you rotate fast it works very bad, sometime the view is hidden sometimes not. So there is my questions :
On a nav based app, what is the best way to make only one controller landscape capable ?
I use :
[self.navigationController pushNavigationController:landscapeController];
Is there a another way not to push the ladnscapeController so that it won't be navController dependant anymore ?
Thanls for all, any help will be welcome.

use this code befor you push next controller
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;
use this code when pop to back controller
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationIsPortrait(UIInterfaceOrientationMaskPortrait);

Related

Landscape in Nav Based app ios

i'm in really bad situation, i have to submit my app, but i have discovered one problem :
My app is a Nav based app, in this app i want only one controller to be able to landscape :
--> Root Controller --> WelcomeController --> LandscapeController
In the landscape controller i have set two views inside the main view, one is portrait mode to tell the user he has to turn the device, the other is set to landscape (in the main view which is in portrait mode)
I have subclassed my navigation controller : MyNavController in which i have set :
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
So the controller on top of the hierarchy decide if the navController can rotate or no.
in my app delegate i have set :
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return (UIInterfaceOrientationMaskAll);
}
so i can have both orientation on my app.
In the Welcome Controller i have set :
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskPortrait);
//return (UIInterfaceOrientationPortrait);
}
- (BOOL)shouldAutorotate
{
return YES;
}
Which means the Welcome Controller can only be in portrait mode.
In the Landscape Controller i have set :
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft;
}
- (BOOL)shouldAutorotate
{
return YES;
}
so that the landscape Controller can rotate.
I call Landscape controller using :
LandscapeController *aLandscapeController = [LandscapeController] allo] init....
[self.navigationController pushViewController:aLandscapeController animated:YES];
It works, but sometimes believe me or not, when i push the landscape Controller and that i turn the device, it is the Welcome Controller which become landscape and take only half of the screen because it can't be in landscape and this so bad to me you can't imagine.
What can i do to avoid that ?
i'll take any help, i'll make donation if i have to.
Thank you very much.
Second sentence of the UINavigationController documentation: "This class is not intended for subclassing." Have you tried it without your override to the navigation controller's orientation behavior? Maybe the navigation controller does the right thing by default, which is to query the child controllers to determine what the orientation should be. But by overriding it, you are breaking the correct behavior, which is more complex than you expected during view transitions.
Keep everything the same except use a standard UINavigationController instead of your subclass. Or if your subclass can't be removed for other reasons, for a quick test try commenting out this code:
/*- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}*/
But you can't rely on anything working right if you subclass a class that the documentation says you shouldn't.

Stop a modal UIViewController from rotating

The modal UIViewController's parent auto-rotates, but when the modal VC is up I only want it to appear in portrait and not be rotatable. I have tried simply returning NO from shouldAutorotate... in the modal VC, but no joy. Supporting iOS 5+.
Any help greatly appreciated.
Basically, if you presenting Modal Controller on Any Container Controllers (i.e. UINavigationController) it have autorotation method return YES by default. So you have to make subclass for your UINavigation Controller and Deprecate Autorotation there.
For iOS 5 use method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return (toInterfaceOrientation == UIInterfaceOrientationPortrait) : self.enableRotations;
}
for iOS 6:
- (NSUInteger)supportedInterfaceOrientations {
switch ([UIDevice currentDevice].userInterfaceIdiom) {
case UIUserInterfaceIdiomPad: return UIInterfaceOrientationMaskAll;
case UIUserInterfaceIdiomPhone: return UIInterfaceOrientationMaskPortrait;
}
}
- (BOOL)shouldAutorotate {
return NO;
}
It can be doable, but I would think twice before doing that.
As a user;
I launch the app and use in landscape.
trigger an event (eg. press button).
see a view which is portrait.
rotate the phone and look in portrait.
close the modal view.
get landscape view again.
need to rotate the phone again
Bad usability...
you have to present that new controller on a new navigation controller.This will solve your problem

Is it possible to have different orientations for viewControllers inside UINavigationController?

I want all view controllers to support only portrait mode, except one view controller lets call it "LandscapeSupportViewController" that should support also landscape mode.
The problem is when I'm in LandscapeSupportViewController in landscape mode and then push a new view controller that only support portrait mode, the pushed view controller will be in landscape mode also! How could I force it to be portrait?
I saw few apps that do it, take for example Skype iPhone app, the Messages tab is portrait only -> then if you press to enter the message itself you get a view controller that support landscape also because it make sense to enable landscape mode when user is chatting -> then if you press to view the persons profile, a new view controller will be pushed but in portrait! the same happen if you go back, you will forced to return to portrait even if you came from landscape...
Thanks
I'd had students try to accomplish exactly what you are trying to accomplish, and after much research, the general consensus is: this is a bad idea and requires a lot of (App Store legal) hacks to accomplish, and still doesn't turn out too pretty (status bar, for example, screws up). You'll notice in the Skype app that when you go into the IM section, rotate to landscape, and hit back, the UI "snaps", or sort of gets instantly reloaded.
This is not a good user experience, and I'd recommend rethinking your design to be more in line with what Apple recommends.
If i got you correctly you want to change device orientation in some conditions.
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationPortrait animated:NO];
set your own orientation using above line, just put this lines inside the if condition. condition is depends on you.
Thank you!!
Write this lines before you push viewController which supported only portrait From landscapeViewController
[appdel.navigationController.view removeFromSuperview];// This navcontroller used with rootviewcontroller
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
[ [UIApplication sharedApplication].self.delegate.window addSubview:appdel.navigationController.view];
self.navigationController.navigationBar.hidden=NO;
Here is a solution.
You can add a category for UINavigationController which manages the view controller orientation. See code below:
#interface UINavigationController (MyViewOrientations)
#end
#implemetation UINavigationController (MyViewOrientations)
- (BOOL)supportLandscapeModeForViewController:(UIViewController *)controller {
return [controller isKindOfClass:[LandscapeSupportViewController class]]
}
- (NSUInteger)supportedInterfaceOrientation {
UIViewController *controller = [self visibleViewController];
NSUInteger orientationMasks = UIInterfaceOrientationMaskPortrait
if([self supportLandscapeModeForViewController:controller]) {
orientationMasks |= UIInterfaceOrientationMaskLandscapeLeft;
orientationMasks |= UIInterfaceOrientationMaskLandscapeRight;
}
return orientationMasks;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
UIViewController *controller = [self visibleViewController];
if([self supportLandscapeModeForViewController:controller]) {
return UIInterfaceOrientationLandscapeLeft; // Your call
}
else {
return UIInterfaceOrientationPortrait;
}
}
- (BOOL)shouldAutorotate {
UIViewController *controller = [self visibleViewController];
return [self supportLandscapeModeForViewController:controller];
}
#end
If the situation is more complex, different views support different orientations. You can override "supportedInterfaceOrientation", "preferredInterfaceOrientationForPresentation", "shouldAutorotate" in your view controllers, and delegate calls from UINavigationController category code with "visibleViewController".

Iphone: Cannot switch back from my landscape view to a portrait view

I am working on an app (my first one), which is basically a TabBar app.
To be more precise there are:
- a login view controller
- a tab bar controller (when login is done)
- a landscape view controller that is used when the first itel of the TabBar is switch from Portrait to Landscape.
So, when I am in the first tab, I need to be able to move to landscape view to display some other data. In my tab bar controller, I have implemented those methods:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if([self selectedIndex] == 0)
return YES;
return NO;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
// Get AppDelegate
MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
// Remove TabBarView and add graph Landscape View
[self.view removeFromSuperview];
[delegate setSubViewLandscapeViewController];
}
}
In the delegate, I have implemented the setSubViewLandscapeViewController and the setSubViewTabBarController:
- (void)setSubViewTabBarViewController {
[window addSubview:[tabBarController view]];
}
- (void)setSubViewGraphLandscapeViewController {
[window addSubview:[landscapeViewController view]];
}
I want the landscapeViewController to display only in landscape mode, I have then (in my landscapeViewController):
- (void)viewWillAppear:(BOOL)animated {
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
NSLog(#"willRotateToInterfaceOrientation");
}
A part of this works fine, I mean the switch from portrait to landscape is ok (when I am in the first tab), the tabbarcontroller is remove from the SuperView and the landscape view is added as a subview instead.
The thing is... I do not know how to switch back to portrait mode (and then load the previous controller, the tabBar one using the setSubViewTabBarViewController of my delegate). It seems none of the willRotateToOrientation, willRotateFromOrientation, .. are triggered when I actually move the device from the landscape view...
In short, when I am in the landscape view I do not know what to do to move back to the tabbar view... I am kind of stuck in the landscape view once I am in this one.
Thanks a lot for your help,
Luc
Look at the pie chart in CPTestApp-iPhone in the examples folder. It handles rotation by implementing -(void)didRotateFromInterfaceOrientation: and resizing the graph after a rotation.
Well, I managed to get a solution for this problem.
In fact, while moving from portrait to landscape I removed the tabbarcontroller from window subview and add the landscapeviewcontroller instead.
It seems it was not the correct thing to do.
Instead, I add the landscapeViewController as subview of the tabbarcontroller and remove it when going from landscape to portrait.
I still have a problem however with the y position of the landscape view which seems to changes when I do several decive rotation in a row....
Regards,
Luc

How to implement UIViewController rotation in response to orientation changes?

My app has about 10 different UIViewControllers, just one of which I want to switch to landscape mode if the device is rotated. (All the rest, I want to keep in portrait.)
In order to implement rotation on that one view, I needed to implement its controller's 'shouldAutorotate' method and return YES. Since this view is accessed via a navigation controller, I also needed to create a subclass of UINavigationController that implements 'shouldAutorotate' and return YES.
This solution works, but too well. I find that all of the UIViewControllers I push onto my subclass of UINavigationController respond to rotation, even if I implement 'shouldAutorotate' and return NO. (Remember: I only want one particular UIViewController to respond to rotation, not every one in the navigation controller's stack.)
So, my question is: how do I best do this? All the solutions I can come up with seem 1) cumbersome, and 2) worse, don't seem to work.
Thanks very much.
when you implement UINavigationController, this class is your the parent that controls all the children viewControllers that will be pushed into the stack. Therefore, the RootViewController is the only controller that say Yes or No to autorotation. Even if, you are passing Yes to auto-rotation in children view controllers, they don't count!
This is the nature of UINavigationController. So to change it, you have two choices:
1- Manually manipulate it, which requires you to go through some cumbersome codes.
2- Change your design so that it is more compatible to UINavigationController. That single view that should rotate, should be get called by the RootViewController (not the Navigation Root View Controller -- they are named the same, but are totally different), the view that hosts NavController. And when the device rotates, it will either push the NavController to the view or the other.
3- The other method, which will also work, but is not recommended because it violates the concept of MVC, is that your NavController can listen to Notifications. That particular child view that CAN and SHOULD rotate can cast a notification -- e.g. rotateMe, and once the NavController hears it, it rotates.
As I said, it will work, but it violates the MVC model -- which is fine to Apple, but from programming perspective is not recommended.
If you need further explanation about either of them, please let me know.
I do this by having a root view controller (this could be a UITabBarController) and in it's viewDidLoad method i subscribe to rotation events:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification"
object:nil];
Then in the didRotate: method i look at which view controller is visible when the rotation happened, and what orientation the phone is in:
- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
/*
DEVICE JUST ROTATED TO PORTRAIT MODE
orientation == UIDeviceOrientationFaceUp ||
orientation == UIDeviceOrientationFaceDown
*/
if(orientation == UIDeviceOrientationPortrait) {
/*
DEVICE JUST ROTATED TO LANDSCAPE MODE
*/
}else if(orientation == UIInterfaceOrientationLandscapeLeft ||
orientation == UIInterfaceOrientationLandscapeRight) {
}
}
Within that didRotate: you can look at which is the visible viewController and do what you want from there.
I present a modal view controller in landscape mode when a particular view controller is visible and the phone is rotated into landscape. If any other view controller is visible, i ignore the event.
I force my modal view controller to display in landscape mode in its viewWillAppear method - i can give anyone this code if they want it.
Hope this helps.
Dave
Returns YES on the navigation controller subclass only if [[navController topViewController] isKindOfClass:[RotatingViewController class]]
I have seen examples using the way you are doing it but couldn't get it to work properly. I found the better way to do it from Apples examples. Basically you implement a presentModalViewController and create another view. UIKit does a basic rotation animation and fades between the view. You have to implement the rotated view as a delegate class so it can call back to its calling class to dismiss it and update the orientation.
- (void)orientationChanged:(NSNotification *)notification
{
// We must add a delay here, otherwise we'll swap in the new view
// too quickly and we'll get an animation glitch
NSLog(#"orientationChanged");
[self performSelector:#selector(updateLandscapeView) withObject:nil afterDelay:0];
}
And then to display a landscape screen:
- (void)updateLandscapeView
{
PortraitView *portraitView = [[PortraitView alloc] init];
portraitView.delegate = self;
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController: portraitView animated:YES];
isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
[portraitView release];
}