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.
Related
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;
}
If you have an app with one UIWindow, my understanding is that the rootViewController of that UIWindow will be the UIViewController that receives the rotation/orientation methods like shouldAutoRotate, shouldAutoRotateToInterfaceOrientation, etc.
I'm writing an external library and there is an instance where I create another UIWindow object, set its rootViewController, and make it key and visible. It seems like the rootViewController of the original window is the one that still gets sent the rotation methods and not the new one.
I want to be able to control whether the application can rotate or not while the new window is visible, but it seems like the original window's rootViewController still has control over that. I've tried setting the original window's rootViewController to a rootViewController that prohibits rotation of the screen while my new window is visible and resetting the original window's rootViewController to its original rootViewController but that causes some problems of its own.
Does anyone know how to make a certain UIViewController the one in charge of app rotation?
How are you presenting the new viewController? According to the documentation,the only viewControllers that are asked about supportedInterfaceOrientations are the root view controller, or a view controller that fills the screen. So, on an iPhone, your new viewController should receive the supportedInterfaceOrientations call if it is filling the screen (e.g., presented modally).
shouldAutoRotateToInterfaceOrientation was deprecated as of iOS 6, so you should override supportedInterfaceOrientations instead.
This has worked for me...
In my Case, the destination view appears correct but the status bar and also the UIKeyboard keeps the landscape configuration, making a real mess.
Working around After thousands of recommendations about statusBarOrientation and references read... https://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html
"The setStatusBarOrientation:animated: method is not deprecated outright. It now works only if the supportedInterfaceOrientations method of the top-most full-screen view controller returns 0. This makes the caller responsible for ensuring that the status bar orientation is consistent."
statusBarOrientation only works if supportedInterfaceOrientations returns 0, so... that give us a guess.
If statusBarOrientation is not as expected, one zero return will do it (if always return 0, the view wont rotate, so:
// if deviceOrientation is A (so I expect statusbarOrientation A
// but statusbarOrientation is B
// return 0
// otherwise
// return user interface orientation for A
- (NSUInteger)supportedInterfaceOrientations {
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
UIInterfaceOrientation statusBarOrientation =[UIApplication sharedApplication].statusBarOrientation;
if(deviceOrientation == UIDeviceOrientationPortrait || deviceOrientation == UIDeviceOrientationPortraitUpsideDown){
if(statusBarOrientation != UIInterfaceOrientationPortrait ||statusBarOrientation != UIInterfaceOrientationPortraitUpsideDown){
return 0;
}
}
// otherwise
return UIInterfaceOrientationMaskPortrait;
}
Now, in viewDidAppear (believe me, I use this call even when the keyboard notification is recived:
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;
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
I'm creating an iOS application and i've encountered the following problem:
The project is set to be a portrait mode application.
I have a UIWebview, which loads a webpage with a youtube video in it. When the video is clicked it starts in the youtube player. When the video changes to landscape mode and 'done' is clicked it returns to the UIWebview showing it in landscape.
I do receive a NSNotification when the video is finished but I'm not able to force portrait mode.
This should not be possible, how can I allow youtube to be in landscape but force the app to stay in portrait mode? Or is there another workaround?
EDIT:
After contact with Apple it turned out to be a bug and is reported.
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
is private API . I would not recommend using it.
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window insertSubview:view atIndex:0];
is much better way i think. It forces the app to adjust its orientation.
I think you can use NSNotification as you said and in called method use this line of code for now to force view to portrait
- (void)YourMethodCalledByNSNotification
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationPortraitUpsideDown)
{
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
}
}
Hope that Helps
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.