supportedInterfaceOrientations method not working on iOS 6 UIViewController - uiviewcontroller

In iOS 5, my application I used the method to change my orientation:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
In iOS 6 I think I'm supposed to do this, but it does nothing! My app is rotated not the way I want it to be.
- (BOOL) shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight;
}

It's how I was adding my viewController.
I replaced this line code:
[window addSubview:viewController.view];
by this line:
[window setRootViewController:viewController];

When the device changes orientation, in iOS 6, the system asks the application which orientation it supports. The application will return a number of orientations it accepts.
How does the application determine it's orientation?
First the application checks it's info.plist orientations (this is very important to determine which orientation to use for launch.
Secondly it asks it's application delegate for it's orientations, this can be specified via the -(NSUInteger)application:supportedInterfaceOrientationsForWindow: method. This effectively overrides the info.plist setting, this implementation is optional. By default iPad apps orientate in all directions and iPhone in all but upside down.
Lastly the application delegate queries it's top most view controller, this can be a UINavigationController or a UIViewController... etc, then it specifies how to be presented and if it wants to autorotate. These UIViewControllers can use shouldAutorotate: and supportedInterfaceOrientations methods to tell the app delegate how to present it.
You must make sure you set the root view controller of your window.
Also if you are presenting any other view controllers in full screen such as a modal view controller, this is the one responsible for determining orientation changes or not.

For me the solution worked, the case was different a bit, because I had a UINavigationController.
My case was that I needed all Portrait window except one. I had to enable all landscape and the portrait orientations in targets (otherwise it crashes on the only landscape view).
So in this case:
create a subclass for UINavigationController,
insert the rotationspecific stuff there
and use the subclass instead of UINavigationController.

If you are using a UINavigationController, it sends the supportedInterfaceOrientations: message to the UINavigationController itself rather than the top most view controller (i.e. what you would actually want it to do).
Instead of having to subclass UINavigationController to fix it, you can simply add a category to UINavigationController.

I created a new variable in my singleton: canRotate, and I set it to YES when I want the viewcontroller to support some orientation.
In appDelegate I added this:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if([[SharedData sharedInstance] canRotate])
{
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight);
}
return UIInterfaceOrientationMaskPortrait;
}
in my case it works. I have a custom tabbar and UINavigationviewControllers are added as subviews to rootViewController.

I know this sounds pretty elementary, but I was wracking my brain to figure out orientation issues while testing on my iPhone - I had the physical auto lock mode in Portrait mode - so nothing I changed programmatically mattered - thought this should be troubleshooting step number 1!

Related

Libgdx, Setting the full screen resolution during runtime causes application to render at the wrong size

I am only using the desktop Application, no mobile.
I am experimenting with letting the user set the screen resolution during run time. I give him the Display Modes available and he applies one. This part actually works. The problem occurs when i save this mode and try to set this display mode the next time they launch the game.
I am using preferences to store the mode the user selected. I am unable to access preferences before the Create method in my Game class, or in the DesktopLauncher Object, where you normally set up the config file and pass it into the application. So my DesktopLauncher looks like this.
val config = Lwjgl3ApplicationConfiguration()
config.setFullscreenMode(Lwjgl3ApplicationConfiguration.getDisplayMode())
Lwjgl3Application(MainGame(), config)
I use the current screen resolution on the creation of the application. Then in my Create method in my MainGame class i get the mode they set from preferences and i set it like so...
override fun create() {
var modes = Gdx.graphics.displayModes.toList()
val mode = Gdx.graphics.displayMode
val preference: Preferences = Gdx.app.getPreferences("screenPreference")
val screenWidth = preference.getInteger("width", mode.width)
val screenHeight = preference.getInteger("height", mode.height)
val refreshRate = preference.getInteger("refreshRate", mode.refreshRate)
modes = modes.filter { it.width == screenWidth }
modes = modes.filter { it.height == screenHeight }
modes = modes.filter { it.refreshRate == refreshRate }
if (modes.isNotEmpty()) {
Gdx.graphics.setFullscreenMode(modes[0])
}
....
}
To summarize i get the list of modes, i pull from preferences what was set last, and i filter the list according to what was in preferences. This should leave one item left in the list and i apply it. If for some reason the list is empty, then i don't set it, or there is no preference set i just apply the current mode again.
This is where the weird stuff happens. I have checked all the numbers when creating my screens and cameras, and they are all correct. I do receive the correct resolution, but the application doesn't render correctly. Below are a couple examples of what happens.
In the first image you see the bounds of the application to the screen. My application only renders in the bottom corner, and the rest is black. What happened to achieve this effect is i started the application with a smaller resolution than my native resolution, so 1280x1024, then in my create method i set the application full screen mode to 1920x1080 before building the rest of my application. I have checked my cameras and my viewports, and they all have the resolution 1920x1080, but the image is not filling the entire screen.
And a second.
This one is what happens when i reverse the settings. So i start at native resolution 1920x1080, and in my create method i set it to 1280x1024, again before creating the rest of my application. This gives me black bars on both sides of the image like id expect, but the application is HUGE, and only a portion of it fits in the window, the rest goes out of bounds, as depicted by the dotted lines.
It will remain like this the entire time, unless i change the resolution while the application is running, it will then correct itself for the rest of the applications life.
I am confounded by this effect i am getting, and am looking for an answer as to why, or how to fix it.
I found the issue that was causing the image to render incorrectly. I was setting the display mode in the create() function in my main game class. This function is not run on the rendering thread, and you do not want to use Gdx.graphics on anything other than the rendering thread, as described in the libgdx wiki https://github.com/libgdx/libgdx/wiki/Threading
There is a function where you can pass in a lambda to be run on the rendering thread.
Gdx.app.postRunnable {
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode(modes[0]))
}
After passing that into postRunnable the game renders correctly on launch.

iOS 8 vs iOS 7 Autorotation

Here is a simple single view controller app :
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor greenColor];
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight;
}
The outputs are so different in iOS 8.
It's got to do with the difference in UIWindow bounds on iOS 8 vs iOS 7. How do I get iOS 7 like behavior ?
This appears to be a bug in Xcode 6 or iOS 8. After switching to storyboards from xib, the problem disappeared.
In IOS8 the list of possible orientations should be in the Info.plist file, the method shouldAutorotate return YES by default.
Take a look on the discussion and documentation below:
https://stackoverflow.com/a/24467576/3330421
UIKit Reference:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/supportedInterfaceOrientations
When the user changes the device orientation, the system calls this
method on the root view controller or the topmost presented view
controller that fills the window. If the view controller supports the
new orientation, the window and view controller are rotated to the new
orientation. This method is only called if the view controller's
shouldAutorotate method returns YES.
Override this method to report all of the orientations that the view
controller supports. The default values for a view controller's
supported interface orientations is set to
UIInterfaceOrientationMaskAll for the iPad idiom and
UIInterfaceOrientationMaskAllButUpsideDown for the iPhone idiom.
The system intersects the view controller's supported orientations
with the app's supported orientations (as determined by the Info.plist
file or the app delegate's
application:supportedInterfaceOrientationsForWindow: method) to
determine whether to rotate.

Allow only one uiviewcontroller to rotate

I'm working on an app using IIViewDeck and I only want one view controller to rotate. It's ios 6 and I was able to work out the shouldRotate cascading to get the one that I need to rotate successfully.
My problem is that if I'm in landscape then switch to another view controller, the app remains in landscape. I need to force it back to protestor but I haven't had any success. I thought the simplest approach would be to do some 'force portait' code in the rotatable view controller's viewWillDisapper but I just don't know how to enforce.
Any suggestions?
Your question is also asked here
In all your VC's say yes to rotation, but use an NSUInterger to set the mode like so:
The answer that questions has is below:
-(BOOL)shouldAutorotate
{
return YES;
}
//keep the below code in all your VC's that you do not want to have the rotation capability
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}

UIViewController device rotation delegate methods are not getting called in iOS5

I am developing an iPhone application. In this application, UIViewController (vc1) presents another UIViewController (vc2). vc1 supports both Portrait and Landscape orientations; vc2 supports only Portrait orientation.
When vc2 is presented, it asks vc1: shouldAutorotateToInterfaceOrientation: and this returns YES
In iOS5 (Beta 7) willRotateToInterfaceOrientation:, didRotateFromInterfaceOrientation: are not getting called for this sequence. But, this works fine in iOS4. Is this a bug in iOS5?
I had reported a bug to Apple and I got the following reply:
"Engineering has determined that this issue behaves as intended based on the following information:
The presentation behavior is correct - if it behaved differently in previous versions, that was a bug. The arguably unexpected change in behavior regards the dismiss of VC1 which no longer gets rotation callbacks but it will layout in portrait.
There are other ways to determine what your orientation is when a view controller lays itself out. For various reasons, relying on the rotation callbacks proved to be problematic.
In general, viewController rotation callbacks occur in two cases:
The device orientation changes for view controllers in the window hierarchy
Mixed interface orientation presentations. (Bottom controller only supports portrait, device is in landscape, and a view controller that supports landscape is presented.) However this is arguably a misfeature.
Try using viewWillLayoutSubviews: in iOS 5."
I had faced a similar issue when testing my app on iOS5. The layout of subviews in the main view controller used to get messed up if the orientation changed when a modal view controller was active.
What I did was to store the current orientation flag in the main controller. This flag is updated in two places in the main controller
willAnimateRotationToInterfaceOrientation:
viewWillLayoutSubviews (this is on iOS5 only)
I write all the logic to adjust the subviews by comparing the current orientation with the stored value. If they are different - update the stored orientation, update your subviews.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Any orientation is OK
return YES;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
portrait = UIInterfaceOrientationIsPortrait(toInterfaceOrientation);
// Code to update subview layout goes here
}
-(void)viewWillLayoutSubviews
{
BOOL isPortraitNow = UIInterfaceOrientationIsPortrait(self.interfaceOrientation);
if(isPortraitNow != portrait)
{
DLog(#"Interfaceorientation mismatch!, correcting");
portrait = isPortraitNow;
// Code to update subview layout goes here
}
}

Only direct subviews of the MainWindow view gets orientation set by iOS?

Whenever my app is rotated, only the viewController of which I've added his view as a subview to the mainwindow gets his interfaceOrientation property updated, the rest remains ignorant of the fact the device has been rotated.
Is it my responsibilty to notify other objects of the change, and if so, what's a nice way to do it?
I've looked into setting interfaceOrientation of my children-viewcontrollers but that's readonly.
Thanks in advance,
I found that calling
willRotateToInterfaceOrientation
and/or
didRotateFromInterfaceOrientation
of the view controller you are going to show worked for me. In my case I was using a Navigation Controller so it was easy to keep track of what was going to be shown to the user next. Below is some code from my project.
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
[viewController willRotateToInterfaceOrientation:
[self interfaceOrientation] duration:0];
}