I have an app with all screens in portrait except for one. In iOS 8 that one landscape page appears fine, until the device gets rotated in any direction. The view rotates on a weird axis and parts of it go off screen.
I've tried updating the frame of the view in viewWillTransitionToSize, but it just causes even more issues, changing the frames of subviews to be super crazy.
Some solutions have suggested doing this:
- (void)applicationDidChangeStatusBarOrientation:(NSNotification *)notification
{
[UIViewController attemptRotationToDeviceOrientation];
}
But that didn't work for me. Any ideas? Thanks!
The offending line will be in your AppDelegate didFinishLaunchingWithOptions function and looks like the following:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
Please try this.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
This will allowed for both left and right landscape (but it deprecated since iOS6).
Or this one but I'm not test it.
- (BOOL)shouldAutorotate {
return NO;
}
Related
The actual title for this question is longer than I can possibly fit:
Launching an app whose root view controller only supports portrait-orientation but which otherwise supports landscape orientations on an iPhone 6 Plus while the home screen is in a landscape orientation results in a limbo state where the app's window is in a landscape orientation but the device is in a portrait orientation.
In short, it looks like this:
When it is supposed to look like this:
Steps to Reproduce:
iPhone 6 Plus running iOS 8.0.
An app whose plist supports all-but-portrait-upside-down orientations.
The root view controller of the app is a UITabBarController.
Everything, the tab bar controller and all its descendent child view controllers return UIInterfaceOrientationMaskPortrait from supportedInterfaceOrientations.
Start at iOS home screen.
Rotate to landscape orientation (requires iPhone 6 Plus).
Cold-launch the app.
Result: broken interface orientations.
I can't think of any other way to enforce a portrait orientation except to disable landscape altogether, which I can't do: our web browser modal view controllers need landscape.
I even tried subclassing UITabBarController and overriding supportedInterfaceOrientations to return the portrait-only mask, but this (even with all the other steps above) did not fix the issue.
Here's a link to a sample project showing the bug.
I had the same issue when launching our app in landscape on an iPhone 6 Plus.
Our fix was to remove landscape supported interface orientations from the plist via project settings:
and implement application:supportedInterfaceOrientationsForWindow: in the app delegate:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
Apparently the information in your plist is to specify what orientations your app is allowed to launch to.
Setting the statusBarOrientation of the UIApplication seems to work for me. I placed it in the application:didFinishLaunchingWithOptions: method in the app delegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
application.statusBarOrientation = UIInterfaceOrientationPortrait;
// the rest of the method
}
This appears to be a bug in iOS 8 when using a UITabBarController as a root view controller. A workaround is to use a mostly vanilla UIViewController as the root view controller. This vanilla view controller will serve as the parent view controller of your tab bar controller:
///------------------------
/// Portrait-Only Container
///------------------------
#interface PortraitOnlyContainerViewController : UIViewController
#end
#implementation PortraitOnlyContainerViewController
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#end
// Elsewhere, in app did finish launching ...
PortraitOnlyContainerViewController *container = nil;
container = [[PortraitOnlyContainerViewController alloc]
initWithNibName:nil
bundle:nil];
[container addChildViewController:self.tabBarController];
self.tabBarController.view.frame = container.view.bounds;
[container.view addSubview:self.tabBarController.view];
[self.tabBarController didMoveToParentViewController:container];
[self.window setRootViewController:container];
I only want my app to open in landscape mode (and not exhibit the problem you describe above on the iPhone 6 Plus), so I set Landscape (left home button) and Landscape (right home button) as the only orientations allowed in my app's PLIST file. This fixes the orientation problem when my app opens. However, I need my app to support portrait mode for one view only since I display a UIImagePickerController in my app, which Apple requires to be shown in portrait mode on iPhone.
I was able to support portrait for that one view only, while keeping my app opening in landscape mode, by including the following code in AppDelegate:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskAll;
}
}
I had a very similar problem. I wanted to force portrait mode everywhere except for playing back videos.
What I did was:
1) to force the app orientation to be in portrait in the AppDelegate:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if ([window.rootViewController.presentedViewController isKindOfClass:[MPMoviePlayerViewController class]])
{
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskPortrait;
}
2) launching an empty modal view controller fixed the problem in my case.
I launch it in the viewDidLoad of the first view controller that is on the root of my NavigationViewController (the first view controller visible after the application launches):
- (void)showAndHideNamelessViewControllerToFixOrientation {
UIViewController* viewController = [[UIViewController alloc] init];
[self presentViewController:viewController animated:NO completion:nil];
[viewController dismissViewControllerAnimated:NO completion:nil];
}
Please try the following code.
Probably this problem caused by size of keywindow on landscape launch.
// in application:didFinishLaunchingWithOptions: ...
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
[self.window setFrame:[[UIScreen mainScreen] bounds]]; //<- ADD!!
No luck for me the workaround by Jared using a generic container view controller. I've already subclassed tab bar controller with supportedInterfaceOrientations with no luck as well. Regardless of orientation of the 6+ after launch the tab bar's window is reporting frame = (0 0; 736 414)
So far the only workaround I've found is to force the window frame after makeKeyAndVisible
[self.window makeKeyAndVisible];
self.window.frame = CGRectMake(0, 0, MIN(CGRectGetWidth(self.window.frame), CGRectGetHeight(self.window.frame)), MAX(CGRectGetWidth(self.window.frame), CGRectGetHeight(self.window.frame)));
I got same bug on my app, I figured it out with this solution
Firstly it didn't work but after some dig I have to do it on initial controller after splash screen.
Answer is OjbC language let me update it to Swift
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
Don't forget that should on the initial view controller.
For myself, I was having the same issue as jaredsinclair, but subclassing a UIViewController with the supportedInterfaceOrientations method was not solving the issue. Instead I did exactly what he did in my appDidFinishLaunching method of my AppDelegate and added my UITabBarController as a child to a normal UIViewController rather than his subclass and it worked!
I'm in the same situation, and doing [self.window setFrame:...] doesn't work for me.
Adding the following at the end of application:didFinishLaunchingWithOptions is the only thing I've found that works. It makes the screen blink and isn't exactly clean and efficient.
I added this at the end of application:didFinishLaunchingWithOptions:
UIViewController *portraitViewController = [[UIViewController alloc] init];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:portraitViewController];
[self.navController presentViewController:nc animated:NO completion:nil];
[self.navController dismissViewControllerAnimated:NO completion:nil];
[UIViewController attemptRotationToDeviceOrientation];
I had a similar issue is with my app runs both in landscape and portrait with a UITabBarController as root view controller.
Whenever the app was launched when in Landscape mode, the view was incorrect.
All I had to do:
- remove rootview controller assignment in the XIB.
- Manually add it in once the app is launched:
(void)applicationDidFinishLaunching:(UIApplication *)application {
application.statusBarHidden = YES;
[self.window setRootViewController:self.tabBarController];
That fixed the problem.
Just Remove All the items in Supported interface orientation except what you want (i need only Portrait) in info.plist , it will work for me
just call
[application setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO];
in app delegate method
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
in fact the device now is UIInterfaceOrientationPortrait after Launching ,if you touch an inputField ,the keyboard is portrait layout
I have a tabBarController with 2 elements: KeyPad, List
The KeyPad- needs to be set only to portrait mode. Inside the UINavigationController - I override the methods:
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
List item starts from a RotationNavigationController - here i ve set all the rotations available.
Everything seems to work ok except of one thing:
when navigating from List item Bar Controller (which is in Landscape) to KeyPad item bar controller - KeyPad is also in landscape mode. It seems that the 2 methods (shouldAutorotate & supportedInterfaceOrientations) are not called. How to solve this?
I;ve also added the following in viewWillApppear (inside Keypad - which extend a UIViewController)
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft || [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight){
// Landscape - WHAT TO DO HERE?
NSLog(#"landscape");
}
EDIT:
*Here is what I did for my problem:*
//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO];
//present/dismiss viewcontroller in order to activate rotating.
UIViewController *mVC = [[[UIViewController alloc] init] autorelease];
[self presentModalViewController:mVC animated:NO];
[self dismissModalViewControllerAnimated:NO];
When you move from one orientation tab to another, it's shouldAutorotate is called before anything else. Since you have specified NO. It stays at it's previous orientation. I worked around this by setStatusBarOrientation in the viewWillAppear:
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
But I will have to warn you, this changes some things, like if you are checking for orientation anywhere in your code, you will have to check the statusBarOrientation instead of the orientation of your view/viewController.
So, I've given up on trying to solve an issue I've been having where my controller will rotate without calling my shouldAutorotateToInterfaceOrientation: method, because everyone for a few years has been stating it as an iOS bug.
Now I need to just force rotate my UIViewController. Is there a way I can do that, since the UIDevice instance method has been removed now and I don't know what to do to force rotate my controller.
I'm not sure which UIDevice method that you are saying has been removed, but the following has worked for me (this does use the UIDevice orientation method). Bottom line, if you have a view that only accepts landscape, you can use the following to force iOS to change the orientation. It's curious, but it works. I apologize that I can't give credit to the original author, but once came across this elsewhere on StackOverflow:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)forceLandscape
{
// make sure shouldAutorotateToInterfaceOrientation is configured to accept only landscape
if (UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation]))
{
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];
}
}
The equivalent to force portrait mode (assuming, of course, your shouldAutorotateToInterfaceOrientation accepts only portrait):
- (void)forcePortrait
{
// make sure shouldAutorotateToInterfaceOrientation is configured to accept only portrait
if (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]))
{
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];
}
}
Using xcode7.3 ios9.
I have been trying to force landscape view on and off for over a week now, no hair left :(. For anyone else finding this question and none of the answers work, or any of the other myriad answers on forcing landscape, I finally found something that works here:
http://www.btday.com/how-to-force-view-controller-orientation-in-ios-8/
Suggestion is to put operation in viewDidAppear, but it also seems to work in viewWillAppear and is slightly less ugly.
override func viewWillAppear(animated: Bool) {
UIDevice.currentDevice().setValue(UIInterfaceOrientation.LandscapeLeft.rawValue, forKey: "orientation")
}
If this is wrong/bad, please speak up, only had a mac for a little over a month so I'm a total nuub!
Also note I'm using a UITabBarController and it doesn't seem to matter whether you put this in the tab bar controller or one of it's children
Okay, so here's the situation:
I have an app in which I only want ONE specific view in a UINavigationController to have a landscape orientation. This view is a UIImageView that I'm capturing a signature on (THAT part works awesome). So, like this:
previous view --> signature view --> next view
(portrait) (landscape) (portrait)
I can't seem to find a good way to force the device orientation to landscape on that signature screen. It'll never make sense to have a portrait orientation on the signature view because there's really not adequate room for signing in that screen width.
So, any bright ideas on how to accomplish this? I've considered possibly doing the signature view modally, thus breaking out of the navigation controller. Thoughts?
You can try to force Device to rotate to necessary orientation - but you need to handle it manually (in addition to overriding UIViewController orientation handling methods).
To rotate device you can use next methods:
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
But in it may not work in all situations...
Also available undocumented approach:
[[UIDevice currentDevice] performSelector:NSSelectorFromString(#"setOrientation:")
withObject:(__bridge id)((void*)UIInterfaceOrientationLandscapeLeft)];
Just override this UIViewController method to only return true for landscape like so and the iphone will be forced to rotate to that device orientation, since it has no other option.
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
Unfortunately, all root UIViewControllers inside of a Navigation Controller must support any of their child orientations. This means that the first view controller in your setup must support landscape, otherwise the child will only support portrait.
The best way to implement what you are looking for is to create a UIViewController that displays its content view on a rotated transform, and just default all UIViewControllers in that stack to portrait.
I think you can embed this view inside a view controller and overwrite the ShouldRotateToInterfaceOrientation method.
Good luck!
To use a View in only landscape, I have the following in the ViewController:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
This might be what your looking for.
// Rotates the view.
CGAffineTransform transform = CGAffineTransformMakeRotation(3.14159/2);
self.view.transform = transform;
// Repositions and resizes the view.
CGRect contentRect = CGRectMake(-80, 80, 480, 320);
self.view.bounds = contentRect;
from http://www.iphonedevsdk.com/forum/iphone-sdk-development/1394-landscape-uiviewcontroller-uiview-rotation.html
I have an app that has landscape only views that even starts in landscape. This was working fine in iOS 5.x but stopped working in iOS 6.x
After trying many many things, some more questionable than others, I found a solution that to me is clear and predictable.
I did several things.
-- I kept the views in landscape mode in IB.
-- I checked both landscape modes in the project settings - there a four icons there to control it
-- Orientation mgmt has changed in iOS 6.x. I had to overwrite a few methods to support changing to landscape
this method is for iOS 5.x
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations.
return (interfaceOrientation & UIInterfaceOrientationMaskLandscape);
}
these 2 methods are for iOS 6.x
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger supportedOrientations = UIInterfaceOrientationMaskLandscape;
return supportedOrientations;
}
- (BOOL)shouldAutorotate
{
return YES;
}
-- But the key was to change the logic in the AppDelegate. Original code I had there was adding a subview (controller.view) to the window. This stopped working in iOS 6.x - I changed the call to window.setRootController. That was the final step that sealed it - it would not work without making this final change
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//[self.window addSubview:viewController.view];
[self.window setRootViewController:viewController];
[self.window makeKeyAndVisible];
return YES;
}
The UINavigationController overrides the contain UIViewController orientation settings, so you have to create a custom subclass of UINavigationController with the following for 5.1:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[self topViewController] isKindOfClass:[SigCaptureViewController class]]) {
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
} else {
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
}
For 6.0 and above you need:
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
if ([[self topViewController] isKindOfClass:[EXTRASigCaptureViewController class]]) {
return UIInterfaceOrientationMaskLandscape;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
What I haven't figured out is how to make the force the UINavigationController to rotate. calling [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO] causes the status bar to rotate but doesn't cause the view to rotate.
So I have a UISplitViewController that is the root view of my program. I have set the initial orientation to be LandscapeLeft and have all 4 orientations supported in my plist.
If I launch the app in portrait - the view that is shown is my root view not the detail view which obviously is not what I want. As soons as I rotate the device, everything works from there.
I have scoured this site and others but have found no fixes. The closest I have come is the adding the following to my app delegate:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
if (UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
if (UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation))
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
else
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;
}
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
So this essentially forces it to draw correctly, which works great except for when the app launches with the device laying flat. Then I dont know the portrait vs landscape (and I know of no way to find it). So I basically then cant accurately say what to do.
The larger thing is this above code is a HACK, and there should be a better way, but for the life of me I cant find it.
Any ideas?
Thanks for your code on fixing this for portrait and landscape. I found a quick fix for the issue when its flat on the desk or upside down(not sure who would start the app upside down, but the same issue happens in this case):
if (UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
if (UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation))
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
else if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;
}
else {
if (([UIApplication sharedApplication].statusBarOrientation == 1) || ([UIApplication sharedApplication].statusBarOrientation == 0))
{
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;
}
else {
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
}
NSLog(#"orientation: %d", [UIApplication sharedApplication].statusBarOrientation);
}
In my App, this works because I just wanted to force them into portrait to start and if they move to landscape after that, it rotates and works fine because I support all orientations.
I don't think this will work in all cases, but this might help you get on the right track. You just need to play with the statusBarOrientation value and manually set it based on what you find works if the InterfaceOrientation is not valid(which it isn't if the device is flat on a table or upside down).
Hope that helps...
Edit: Also, I have a need in my App for knowing orientation while they are picking new items in the SplitViewController, so it was being wonky when the device was flat or upside down. And the way I detected if it was flat or upside down was to first see if the orientation was valid like you did:
if (UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation))
But then if its not, I check to see if self.popoverController is nil. If its not, then its in portrait and if it is, then its landscape (based on how the splitViewController works by setting that to nil or not):
if (self.popoverController)
{
NSLog(#"in portrait");
}
else {
NSLog(#"in landscape");
}
This wouldn't work in the App Delegate while the App is launching though because this detailViewController isn't being used at that point. Just thought I'd let you know if it helps out any.
Your RootViewController is showing in place of your detailViewCOntroller?
it seems you're doing something bad... (maybe inverted viewControllers order in SplitViewController.viewControllers ?)
You need to make sure to add your UISplitViewController's view to the UIWindow INSIDE the application:application didFinishLaunchingWithOptions: method of your app delegate (not later), or the split view won't rotate correctly initially.