UINavigation controller with different orientations - objective-c

I want to create a UINavigationController, with a Master View Controller, and a Detail View Controller.
The Master View Controller can be rotated in Portrait and LandscapeRight, while the detail View Controller can only be only be viewed in LandscapeRight (the Detail shows a movie).
What's the best way of setting this up?

I would recommend adding the following lines of code
On your Master View Controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeRight;
}
and on your Detail View Controller
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return interfaceOrientation == UIInterfaceOrientationLandscapeRight;
}
That should do the trick.

in iOS 7, the method
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
has been deprecated. You can however use
- (BOOL)shouldAutorotate
to simply return a yes/no.
and then you can tell the viewcontroller which orientations are acceptable in
- (NSUInteger)supportedInterfaceOrientations

Related

Allow autorotation on just one view controller

In my project I have allowed only portrait rotation, but for one ViewController I would like to enable also landscape. I'm presenting this ViewController as ModalViewController, I've tried using methods - (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation or iOS 6 methods like -(NSUInteger) supportedInterfaceOrientations but nothing actually worked. The view didn't rotate although those methods got called.
After this I've tried to rotate it by myslef with listening to those notifications :
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
but even though I was able to manually rotate the view in method didRotate: it's very messy and I can't rotate the StatusBar.
I would really like to use standard methods like shouldAutorotateToInterfaceOrientation, but I don't know how. Anyone?
Add this in your app delegate.m
# pragma mark - Rotation
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if ([self.window.rootViewController isKindOfClass:[MVYSideMenuController class]]) {
// Get topmost/visible view controller
UIViewController *currentViewController = [self.window.rootViewController.childViewControllers lastObject];
// Check whether it implements a dummy methods called canRotate
if ([currentViewController respondsToSelector:#selector(canRotate)]) {
// Unlock landscape view orientations for this view controller
return UIInterfaceOrientationMaskAllButUpsideDown;
}
}
// Only allow portrait (standard behaviour)
return UIInterfaceOrientationMaskPortrait;
}
-(void)canRotate
{
}
and then add this method
-(void)canRotate
{
// just define the method, no code required here
}
in every ViewController (.m files) where you want to provide rotation. You can also include here -(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation method to react when the device rotates:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
switch (orientation) {
case 1:
case 2:
//NSLog(#"portrait");
break;
case 3:
case 4:
//NSLog(#"landscape");
break;
default:
//NSLog(#"other");
break;
}
}
Subclass a navigation controller for your screen that requires rotation.
In the .m
// Older versions of iOS (deprecated) if supporting iOS < 5
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
// iOS6
- (BOOL)shouldAutorotate {
return YES;
}
// iOS6
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
This overrides the rotation methods set in the summary page for iOS 6.
In iOS 6 the view controllers only look to there parent or root controller for rotation methods
can't you just call the shouldAutoRotateToInterfaceOrientation in the viewDidLoad and viewWillAppear like so:
[self shouldAutorotateToInterfaceOrientation];
that should call the method if its in your ViewController
Implement is in all controller and Return on that interfaceOrientation which you need for a specific controller
For All
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation{
return YES;
}
For Landscape
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
For Portrait
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return ((interfaceOrientation == UIInterfaceOrientationPortrait) || (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown));
}

Force Landscape Orientation on iOS 6 in Objective-C

I have a master view controller that's inside a UINavigationController. In that master view controller, I have a button that pushes a detail view controller that has a UIWebView inside of it. I want this detail view controller to be on landscape mode when it's loaded. Going back to the master view controller, it forcibly goes back again to portrait mode. I'm running iOS 6 on this.
I have seen the other similar questions but it's not working on my end. I have created a LandscapeViewController that's a subclass of UIViewController where I have written these methods:
#pragma mark - Orientation Methods
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotate
{
return YES;
}
This is my code when I push the detail view controller:
DetailViewController *detailVC = [[DetailViewController alloc]
initWithNibName:#"DetailViewController"
bundle:nil];
[self.navigationController pushViewController:detailVC
animated:YES];
I'm thinking on where to subclass my LandscapeViewController on the code above to make it work or on how to properly subclass and push my detail view controller. I can also present my detail view controller modally if it's not possible for the navigation controller to push my detail view controller from portrait to landscape. Where am I doing it wrong?
Considering:
View A: Portrait only - View B: Landscape only
I couldn't do it in the navigation controller. Instead what I did was to open a modal view from view A to view B and force a new navigation controller into this view.
This is working for me in iOS5+.
You need to create a category for the navigation controller like this:
UINavigationController+Rotation_IOS6.h
#import <UIKit/UIKit.h>
#interface UINavigationController (Rotation_IOS6)
- (BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation;
#end
UINavigationController+Rotation_IOS6.h
#import "UINavigationController+Rotation_IOS6.h"
#implementation UINavigationController (Rotation_IOS6)
- (BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
#end
In AppDelegate.m add:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
return UIInterfaceOrientationMaskAll;
}
Then in View A:
- (BOOL)shouldAutorotate {
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
also in View A to open View B do this:
ViewB *vc = [[ViewB alloc] initWithNibName:#"ViewB" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:vc];
[self presentViewController:navigationController animated:YES completion:nil];
And, finally, in View B
- (BOOL)shouldAutorotate {
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
They kinda screwed the pooch in iOS 6 with regard to this. Here's what I've figured out so far:
First off, Apple added the "Supported Interface Orientations" buttons with Xcode 4.5. This corresponds to the "Supported interface orientations" attribute in _info.plist. These buttons must be toggled to the correct choices before any of the rest will work. (If the buttons seem to refuse to toggle it's likely because the info.plist is locked by CVS or some other process.)
Next, the property .window.rootViewController must be set, and must point to the "bottom" view controller in the stack. Generally this will be either a navigation controller or a tab controller.
If the desire is to disable all rotation, this can be done using the buttons, or one can implement, in the "bottom" view controller, the "shouldAutorotate" method and have it return NO. (If the method is omitted then the default is YES.)
In spite of having autorotation disabled with shouldAutorotate, if there is a MPMoviePlayerViewController being displayed, that will autorotate. Only toggling the supported interface orientation buttons appears to prevent this.
If one wants to conditionally autorotate other view controllers it gets messier. Basically, your "bottom" view controller must implement the supportedInterfaceOrientations method and return, based on the current topViewController, the appropriate bit mask. This can be done with a routine that queries the topViewController's old "shouldAutorotateToInterfaceOrientation" method, but it's a bit ugly. And even though this scheme doesn't require modifying the rotating view controller's code, you DO need to modify the VC just "below" the rotated one to implement "supportedInterfaceOrientation", or else that view will be rotated on return. (At least this is a simple copy/paste.) No one seems to have come up with a better, more general scheme, though.

Don't rotate subviews

When i used this code:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration: (NSTimeInterval)duration {
if ((toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) || toInterfaceOrientation==UIInterfaceOrientationLandscapeRight ) {
LeftLVC* vc = [[LeftLVC alloc] initWithNibName:#"LeftLVC" bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
}
}
subviews rotate. In my project subviews must stay fixed portrait.
shouldAutorotateToInterfaceOrientation: is used to specify the supported orientation. If you want to support only the portrait mode only then :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
above method is deprecated in iOS 6.0. Override the supportedInterfaceOrientations and preferredInterfaceOrientationForPresentation methods instead.
For detail read Apple Documentation.
willRotateToInterfaceOrientation:duration: Sent to the view controller just before the user interface begins rotating. Here this method will be called when you move the orientation from landscape to portrait.

iOS rotation using tabBar controller locking to portrait

currently working on an app at work that uses a tab bar controller. The app will not rotate to landscape mode at all - all views inherit from a baseVieController, and in here I have implemented:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return true;
}
Now I know a tabBar controller will not rotate unless all its subviews support the orientation the view is trying to rotate to - my question is this: If I do not implement the - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation method in all subviews, will it lock these subviews to portrait mode, even if I do not specify this as a desired orientation? Therefore locking the whole tabBar controller to portrait. I know similar questions have been asked before, but I couldn't find the answer to this specific question. Thanks in advance.
You can rotate the view, just needs to over ride like below:
Just add the code in the view controller class where you want rotation on (here it is for "SampleClassName")
#interface UITabBarController (rotation)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
#end
#implementation UITabBarController (rotation)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if ([self.selectedViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navController = (UINavigationController *) self.selectedViewController;
if ([[navController visibleViewController] isKindOfClass:[SampleClassName class]])
return YES;
}
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
If you are developing for IOS 5 using storyboards this will help i had the same issue. Typically before storyboards we might add the TabBarController to a uiview or in the appdelegate. With storyboards the storyboard view does not always have to be connected to a viewcontroller.
To fix do this
Add a new class file objective-c class in the subclass field type UITabBarController
1 - In the storyboard select the tab bar controller view
2 - Under custom class change UITabBarController to your newly created class name, i called mine MainTabBarViewController
3 - In you newly created class change this
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
to
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationPortrait)
return YES;
if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
return YES;
return NO;
}
Basically what is happening is you are creating a structure in Interface Builder, but that only gets you part of the way. In this case you will still have to create the companion code. This confused me at first, because i'm used to building views up from scratch using .xib, and would typically configure the tabbarcontroller from the appdelegate.
Also you can conditionally control each of these like this
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
return NO;
if (interfaceOrientation == UIInterfaceOrientationLandscapeRight)
return NO;
if (interfaceOrientation == UIInterfaceOrientationPortrait)
return YES;
if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
return YES;
Just return yes for what you want, return no for what you don't want. or to accept everything return yes .
As far as your parent view controller (in your case - baseViewController) implements this method -
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return true;
}
you have no need to implement this in child view controllers as it supporting all orientation in all child view controllers.

Switching to landscape view and using a tab bar controller

I am using a tab bar controller for my app. If I'm in one of the view controllers called "results" and the ios device rotates to landscape mode, it switches for another view I created called landScape. This has been done within the method
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrient‌​ation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
self.view=landScape;
} else {
self.view=originalView;
}
}
It works just fine whenever I am in that specific controller within the tab bar (example results view controller). However; If I go to another element in my tab bar controller and my phone is tilted in landscape mode and then I decide to go to resultsviewcontroller, it does not call upon my view landScape, instead it tries to autosize the view on its own and it looks awful. Should I call the method -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration within my viewDidLoad method to solve the problem? Or is this completely wrong?
Thank you in advance!
The problem is you're only setting resultsViewController.view when it is the active view controller and detects a rotation. Try this:
- (void)setViewForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
self.view = UIInterfaceOrientationIsPortrait(orientation)
? originalView
: landScape;
}
- (void)viewWillAppear
{
[self setViewForInterfaceOrientation:[[UIDevice currentDevice] orientation];
[super viewWillAppear];
}
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrient‌​ation duration:(NSTimeInterval)duration
{
[self setViewForInterfaceOrientation:toInterfaceOrientation];
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}