I have an iOS application with storyboard. I want my last viewcontroller to stay always in portrait mode. I've been reading and I found that since
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
is deprecated I should use other methods like
-(BOOL)shouldAutorotate
-(NSInteger)supportedInterfaceOrientations
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
but i have tried so many combinations of this methods and I have not been able to do it. So please can someone tell me the correct way?
Since your UIViewController is embedded in a UINavigationController it'll never get called unless you forward on the calls yourself. (A bit of a flaw in UINavigationController in my opinion)
Subclass UINavigationController like this:
#interface RotationAwareNavigationController : UINavigationController
#end
#implementation RotationAwareNavigationController
-(NSUInteger)supportedInterfaceOrientations {
UIViewController *top = self.topViewController;
return top.supportedInterfaceOrientations;
}
-(BOOL)shouldAutorotate {
UIViewController *top = self.topViewController;
return [top shouldAutorotate];
}
#end
If you have UIViewControllers within other UIViewControllers (ie a UINavigationController or a UITabBarController), you will need to proxy those messages to the child object you're implementing this behavior for.
Have you set a breakpoint in your implementations to be sure your view controller is being queried?
In AppDelegate:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;
if(self.window.rootViewController) {
UIViewController *presentedViewController = [[(UINavigationController *)self.window.rootViewController viewControllers] lastObject];
orientations = [presentedViewController supportedInterfaceOrientations];
}
return orientations;
}
In Your ViewController:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Related
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
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 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;
}
I want to play a video from a view controller. When I present it, it is presented like it is a portrait orientation, so view turns. It only happens on iPhones,not the iPads.
There is a ViewController > MyItemsController > VideoController
When I close the VideoController, parent controller (MyItemsController) of the video controller is like:
Storyboard of the view controller is:
And the code is:
-(void)playMoviesForItems:(NSArray *)shopItems{
VideoPlayerViewController* moviePlayer = [self.storyboard instantiateViewControllerWithIdentifier:#"videoPlayerController"];
moviePlayer.modalPresentationStyle = UIModalPresentationCurrentContext;
moviePlayer.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:moviePlayer animated:NO completion:nil];
}
I moved the code into app delegate :
-(void)playMoviesForItems:(NSArray *)shopItems{
VideoPlayerViewController* mp = [[self getStoryboard] instantiateViewControllerWithIdentifier:#"videoPlayerController"];
[mp playMoviesForItems:shopItems];
[self pauseBackgroundMusic];
[self.window makeKeyAndVisible];
[self.window.rootViewController presentViewController:mp animated:YES completion:NULL];
}
This time, everything seem to be ok. Movie is playing, I can hear the sound, but cannot see the video. Why?
While the accepted answer was down voted since it does not answer the question, here's something that works on iOS9 :
The method that gets called when the ViewController is presented (modally) is preferredInterfaceOrientationForPresentation. This method must return an orientation.
You should check the presenter's orientation and return it as preferred:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
UIInterfaceOrientation topOrientation = self.navigationController.visibleViewController.interfaceOrientation;
UIInterfaceOrientation presentingOrientation = self.presentingViewController.interfaceOrientation;
return presentingOrientation ? presentingOrientation : topOrientation ? topOrientation : UIInterfaceOrientationLandscapeLeft;
}
topOrientation contains the visible view controller's orientation, while the presentingOrientation is the orientation of the called to presentViewController:animated...
In general, I advise you to create a "base" UIViewController class, and inherit from it. This way all of your view controllers will benefit from this code.
I had this exactly same problem a few minutes ago.
What happened to me is that I was trying to present the new ViewController with a another ViewController which wasn't in the hierarchy, it only had it's view added as a subview of a third ViewController in the hierarchy. To fix the problem, I just made this last ViewController present the new one.
Example of what not to do:
UIViewController *secondController = [UIViewController alloc] init];
[rootViewController presentViewController:secondController animated:NO completion:nil];
UIViewController *controllerOutsideHierarchy = [UIViewController alloc] init];
[secondController.view addSubview:controllerOutsideHierarchy.view];
[controllerOutsideHierarchy presentViewController:thirdController animated:NO completion:nil];
Example of what should be done instead:
UIViewController *secondController = [UIViewController alloc] init];
[rootViewController presentViewController:secondController animated:NO completion:nil];
UIViewController *controllerOutsideHierarchy = [UIViewController alloc] init];
[secondController.view addSubview:controllerOutsideHierarchy.view];
[secondController presentViewController:thirdController animated:NO completion:nil];
Hope this helps!
Swift 4.1 version
To prevent auto rotation orientation you need to override shouldAutorotate variable
override var shouldAutorotate: Bool {
return false
}
To present view controller in landscape mode you need to override preferredInterfaceOrientationForPresentation variable
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return .landscapeLeft// or .landscapeRight
}
You need to present view controller to get it work(It doesn't work for pushed view controller)
Make sure to add these two in the child view controller:
- (UIInterfaceOrientationMask) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationMaskLandscape);
}
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskLandscape);
}
Try this...
you must add the methods below , in your self.window.rootViewController 'class:
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
if your rootViewController is [UINavigationController class], command+n add a category of UINavigationController ,like my code below
#implementation UINavigationController (Rotation_For_iOS6)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
and then ,go to the viewController.m which u want to be landscape or portrait mode,maybe for u is your VideoController !!!!!!
add methods below:
#pragma mark
#pragma mark ----- Orientation Control For iOS 6/7 -----
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight||toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
now i set my vc supported for landscape mode, last but not least,make sure your project deployment info select the orientation your want.