I have three view controller both are which are pushed on the navigation controller which I have subclassed in order to allow rotation in only Second View Controller, I do it like this,
- (BOOL)shouldAutorotate
{
if ([self.topViewController isKindOfClass:[SecondViewController class]])
return YES;
return NO;
}
I write this piece of code in my Custom Navigation Controller, The problem is that if I open my application in portrait mode and then change the orientation to landscape mode my View Controller does not rotate but even when my Second View Controller opens up it opens in portrait mode although I expect it to open in landscape mode as it supports rotation.
How can I achieve this?
You need to use attemptRotationToDeviceOrientation during navigation. You should override push/pop methods to call attemptRotationToDeviceOrientation with a small UI delay (dispatch_async)
#implementation CustomNavigationController
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[super pushViewController:viewController animated:animated];
[self updateOrientaion];
}
- (nullable UIViewController *)popViewControllerAnimated:(BOOL)animated
{
[self updateOrientaion];
return [super popViewControllerAnimated:animated];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
if ([self.topViewController isKindOfClass:[SecondViewController class]])
return UIInterfaceOrientationMaskAll;
return UIInterfaceOrientationMaskPortrait;
}
- (void)updateOrientaion
{
dispatch_async(dispatch_get_main_queue(), ^{
[UIViewController attemptRotationToDeviceOrientation];
});
}
#end
But when you pop to rootViewController of the UINavigationController supportedInterfaceOrientations is called for the rootViewController. So you also need to implement supportedInterfaceOrientations for the FirstViewController
#implementation FirstViewController
.......
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
#end
Related
I'd downloaded a sample from this tutorial site about rotating the viewcontrollers in navingation controllers.
That sample project functionality
Support potrait for FirstviewController
Support all orientations for second view controller(pushed from first VC)
What I need is,
FirstviewController should support all orientations
Second view controller(pushed from first VC) should support potrait alone.
What I did was, just swapped the codes in the view controllers
In first view controller
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
In Second view controller and
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
But, both the view controllers are rotating.
How can I fix this issue? Any suggestions, please share.
----EDIT----
If I rotate the simulator to landscape before pushing, second VC is also in landscape view.
If I push to second vc in potrait, the second VC is in potrait mode(it's not rotating if I rotate there.)
I put log in customNavigationcontroller NSLog(#"self.topViewController.class %#",self.topViewController.class);. It's only logging for firstview controller after pushing to second vc it's not logging
I suggest an easier way to solve this issue.
Select your project's target and configure it like this:
Then go to Appdelegate.m and paste:
- (UIViewController*)topViewController {
return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
// Handling UITabBarController
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
}
// Handling UINavigationController
else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
}
// Handling Modal views
else if (rootViewController.presentedViewController) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
}
// Handling UIViewController's added as subviews to some other views.
else
{
for (UIView *view in [rootViewController.view subviews])
{
id subViewController = [view nextResponder]; // Key property which most of us are unaware of / rarely use.
if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
{
return [self topViewControllerWithRootViewController:subViewController];
}
}
return rootViewController;
}
}
Now here's the fun part.
Put this method on AppDelegate.m,you can decide the orientation of every controller here.
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
id presentedViewController = [self topViewController];
NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil;
if (window && [className isEqualToString:#"FirstViewController"]) { //FirstviewController should support all orientations
return UIInterfaceOrientationMaskAll;
} else {
return UIInterfaceOrientationMaskPortrait; //Second view controller(pushed from first VC) should support potrait alone.
}
}
You can then remove all the other methods you've added such as shouldAutorotateToInterfaceOrientation, shouldAutorotate , supportedInterfaceOrientations , preferredInterfaceOrientationForPresentation.
All these methods are now all unnecessary.
-----Edit-----
Simply using
[[UIDevice currentDevice] setValue:[NSNumber numberWithInt:UIInterfaceOrientationPortrait] forKey:#"orientation"];
in all view controllers where I need potrait will do the trick. no other things needed.
I am trying to programmatically create UI orientation (portrait and landscape) using Objective C for all devices. Here the problem is I have multiple view controllers. I want to use multiple orientation into particular view controller.
For Ex:
Splash screen (App delegate - Portrait)
Login screen (Portrait)
Home screen (Both)
If I controlled by below method into App delegate root class then I cant enable both orientation into home view controller. Its showing black screen.
- (NSUInteger) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskPortrait;
}
You can try by following implementation. What here doing is, we sends the interfaceorientation we need for the visible view controller from app delegate. For that fist finds the visible viewcontroler and gets its supported interface orientation, If supportedInterfaceOrientations method not implemented in the visible view controller, return default orientation.
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
UIViewController *topController = [self topmostViewController];
if ([topController respondsToSelector:#selector(supportedInterfaceOrientations)]) {
return [topController supportedInterfaceOrientations];
}
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (UIViewController *)topmostViewController {
UIViewController *topController = self.window.rootViewController;
return [self topViewControllerWithRootViewController:topController];
}
- (UIViewController *)topViewControllerWithRootViewController:(UIViewController *)rootViewController {
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.topViewController];// dont use visible view controller, since it will return the presented ViewController, it may be UIAlertController.
} else if (rootViewController.presentedViewController && ![rootViewController.presentedViewController isKindOfClass:[UIAlertController class]]) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
} else {
return rootViewController;
}
}
And if you want to display any view controller with orientation other than UIInterfaceOrientationMaskAllButUpsideDown implement following method in your view controller
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return <whichever orientation you need>;
}
UPDATE:
Make sure that you selected only the orientations you needed for initial view controller in target settings.
Ref: Finding topmost view controller code from this answer with slight modification by addressing UIAlertcontroller presented case
I enabled the landscape orientations for my app at the general target settings:
This is all working fine - the app is available in all selected orientations...
Now I want to disable the landscape mode on only one single view.
I tried the following at the specific view controller:
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Unfortunately without success...
Edit:
The structure of my app is:
UITabBarController
UINavigationController
UITableViewController
UIViewController
Did you put the code above in the ViewController of the view or the NavigationController? You need to put it in the navigation controller, otherwise the navigation controller will rotate, causing the view to rotate as well. Subclass UINavigationController and override shouldAutoRotate:
- (BOOL)shouldAutorotate
{
id currentViewController = self.topViewController;
if ([currentViewController isKindOfClass:[ViewController class]])
return NO;
return YES;
}
Consequently, the navigation controller will only rotate if the displayed view is the view you do not want to rotate.
EDIT
Inside UITabBarController:
- (BOOL)shouldAutorotate
{
if([self.selectedViewController isKindOfClass:[UINavigationController class]]){
UINavigationController *navigationController = (UINavigationController *) self.selectedViewController;
id currentViewController = navigationController.topViewController;
if ([currentViewController isKindOfClass:[ViewController class]])
return NO;
}
}
return YES;
}
I have an iOS app with 2 view controllers namely - FirstViewController and SecondViewController. My window's rootViewController is UINavigationController.
FirstViewController is supposed to work ONLY in portrait mode and SecondViewController ONLY in Landscape mode.
Searching all over Stackoverflow I found out that for iOS6 and above I have to create a category over UINavigationController and override -supportedInterfaceOrientations
THE PROBLEM
Starting from FirstViewController. Now my phone being in Portrait mode, I push SecondViewController, the view loads in portrait mode. Once I rotate my phone to be in landscape the view will rotate to landscape ( and from this point onwards will not return to portrait at all ).
When I pop back FirstViewController will be in Portrait again ( no matter what the orientation of the phone ).
I want that SecondViewController shouldn't be displayed in Portrait mode at all. I've racked my brains all day...cannot find a solution.
APPDELEGATE
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController *vc = [[ViewController alloc] init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:vc];
[self.navigationController setNavigationBarHidden:YES];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
FirstViewController
- (IBAction)btnClicked:(id)sender
{
SecondViewController *vc = [[SecondViewController alloc] init];
[self.navigationController pushViewController:vc animated:NO];
}
#pragma mark - Rotation handlers
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
SecondViewController
- (IBAction)btnClicked:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark - Rotation handlers
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight;
}
UINavigation Category
#implementation UINavigationController (Rotation)
-(BOOL)shouldAutorotate
{
//return [self.topViewController shouldAutorotate];
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
#end
Well, it's a bit late but here is what I figured.
While there are many solutions available for the case when FirstVC is Portrait and Second can be Portrait and Landscape, I couldn't find any good solution to this problem (First Portrait ONLY and Second Landscape ONLY). Here is what I did:
Embed both view controllers in their own Navigation Controllers. Create two new classes, say FirstNavController and SecondNavController subclassing UINavigationController. Use these as your navigation controllers. (If you are using StoryBoards, select the Navigation Controller, go to Identity Inspector and change 'Class' field).
Now, in FirstNavController, add:
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
And in SecondNavController:
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
You will have to present the SecondNavController modally.
Nothing needs to be done in your View Controllers. Make sure you add all required orientations in your application settings.
The only drawback of this method is that both views are not in the same navigation stack, as second was presented modally, so you won't see a back button. But you can add a cancel/dismiss button yourself and call dismissViewControllerAnimated in SecondVC.
in my previous app i have done it
first you need to enable Portrait , Landscape left , Landscape right Orientation to project
Now
set below code to your FirstViewController
-(void) viewWillAppear:(BOOL)animated{
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
}
- (BOOL)shouldAutorotate
{
return NO;
}
set below code to your secondViewController
#define degreesToRadian(x) (M_PI * (x) / 180.0)
#implementation UINavigationController (Rotation_IOS6)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
#end
#interface secondViewController ()
#end
#implementation secondViewController
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
#end
I have iPhone application that supports only Portrait orientation. I want to add to my project view controller that will support only Landscape orientation? Is it possible? If yes how could I achieve that?
I have tried to crate category file like this:
#implementation UINavigationController (Rotation_IOS7)
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
If I do this I get this error:
Terminating app due to uncaught exception UIApplicationInvalidInterfaceOrientation, reason: Supported orientations has no common orientation with the application, and shouldAutorotate is returning YES
I've tried this and it works: http://www.sebastianborggrewe.de/only-make-one-single-view-controller-rotate/
First, add these code to your AppDelegat class.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
// Get topmost/visible view controller
UIViewController *currentViewController = [self topViewController];
// 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;
}
- (UIViewController*)topViewController {
return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
} else if (rootViewController.presentedViewController) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
} else {
return rootViewController;
}
}
Then, in your landscape view controller, add this method
- (void)canRotate { }
I have search through numerous topics and finally found a working solution.
In my example, I have two VC's:
A -> VC that is embedded inside Nav. Controller and should only support Portrait view.
B -> VC that is not embedded inside a VC and should support Landscape only.
I would like to go from view A to view B (by pressing a button) and back to view then A with the specific orientations still correct.
I. Create a Category for UINavigationController and write the following in its .m file: (the code will be automatically called)
- (NSUInteger)supportedInterfaceOrientations
{
NSLog(#"supportedInterfaceOrientations = %d ", [self.topViewController supportedInterfaceOrientations]);
return [self.topViewController supportedInterfaceOrientations];
}
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// You do not need this method if you are not supporting earlier iOS Versions
return [self.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
II.
Create a modal segue between A and B and after that between another one between B and A.
III. Write down in each of the View Controllers .m files the following:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
OR
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
After adding this code. You will be able to change orientation for the single view B.
Edit:
create a category in .h and then implement those methods
use these methods in the view controller where you want to support landscape
#implementation UINavigationController (Rotation_IOS7)
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}