I have a modal view controller (let's call it the popup view controller) presented over my main view controller. At some point, I need so see the main view controller behind the popup. Therefore, I set the modalPresentationStyle property to UIModalPresentationCurrentContext before presenting the popup view controller.
So what happens is that when the device orientation changes while the popup view controller is modally presented, the willRotateToInterfaceOrientation: and didRotateFromInterfaceOrientation: methods are not called. supportedInterfaceOrientations is called amd returns the good value. The rotation is enabled, the supported orientations are correctly set. The popup conIt actually works if I change 'modalPresentationStyle' to the default value, everything works fine, except that obviously I do not see the main view controller behind.
I should add that the main view controller only supports portait, while the popup above it supports all orientations.
On an iOS 5.1 device, the willRotate and didRotate methods are correctly called. It is only on the iOS 6 device that they are not.
Did anybody encountered a similar issue or already needed to display a transparent multi-orientation view controller modally above a single orientation view controller?
Do this...
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation {
UIDevice* thisDevice = [UIDevice currentDevice];
if (thisDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
return true;
}
else
{
return interfaceOrientation == UIDeviceOrientationLandscapeLeft;
}
}
Related
When presenting a modal with UIModalPresentationCustom, it ignores the orientation methods, and displays / rotates to whatever the presenting VC is configured to.
Example:
Presenting VC supports Landscape and Portrait.
Presented VC supports Portrait only (via preferredInterfaceOrientationForPresentation and supportedInterfaceOrientations.
When presenting it in landscape without UIModalPresentationCustom, it rotates the view back to portrait, then presents the VC accordingly. Unfortunately, because I need the presenting VC to stay visible below, I am forced to use UIModalPresentationCustom. And when that happens, the presenting VC is forced into landscape mode, creating a messed up UI and generating constraint issues. And even when presenting in portrait, it becomes allowed to rotate into landscape, ignoring that shouldAutorotate returns NO.
PS: I found a workaround on iOS 7 by adding this method to my App Delegate, but it doesn't fix it on iOS 8.
#implementation UIViewController (customModalFix)
- (BOOL)shouldAutorotate
{
if ([self.presentedViewController isKindOfClass:[IntroViewController class]]) {
return [self.presentedViewController shouldAutorotate];
}
return YES;
}
#end
EDIT: Implementing supportedInterfaceOrientations on the presenting VC doesn't help at all, since it is only called when the view is loaded, not when a VC is about to be presented over it. Still haven't found a solution to this problem.
Maybe I'm late. The point is, when using UIModalPresentationCustom, the presenting VC will not disappear, and the presented VC is not considered to be presented full-screen (even if it does take up the full screen). Thus, it's the presenting VC that is consulted for the supported interface orientations. So the solution can be like:
- (NSUInteger)supportedInterfaceOrientations
{
if (self.presentedViewController) {
return [self.presentedViewController supportedInterfaceOrientations];
}
return [super supportedInterfaceOrientations];
}
If you only use UIModalPresentationCustom to keep the presenting VC visible below, say you need a clear colored VC, my answer here may work for you too:
https://stackoverflow.com/a/29167837/46940801
I want to lock Orientation for entire application if server sends 'Lock' message. When server send 'Unlock' orientation , i want the app to do auto rotate. I stored the value 'Lock' in NSUSerDefaults. In rootViewcontroller , it is working fine like if Lock is true, shouldAutoRotate method returns NO, and view does not rotate. But in other view controller, even lock is true, shouldAutoRotate is returning No, but View is still rotating.
I have created a category of UINavigationController for autorotation so that shouldAutorotate method is called from every class.
- (BOOL)shouldAutorotate
{
NSString *AUTO_ROT =[[NSUserDefaults standardUserDefaults]valueForKey:#"AUTOROT"];
if([AUTO_ROT caseInsensitiveCompare:#"true"] == NSOrderedSame)
{
return YES;
}
else{
return NO;
}
}
Please help me in this.
Are you sure the rotating view controller is also a child view controller of a UINavigationController (i.e. did you push it on the UINagivationController)?
If you presented it using presentViewController:animated:completion: for example the view controller itself would also need to implement shouldAutorotate.
On a side note, be cautious of overriding via category. A much better way would be to subclass UINavigationController and override the method in the subclass.
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;
My whole app is portrait. When you tap an image in a collection view controller, I present the images in a MWPhotoBrowser View Controller modally. So I want to support multiple orientations there. I'm only supporting iOS 6, and have the following orientation methods:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
These get called, but willRotateToInterfaceOrientation and subsequent willAnimateRotationToInterfaceOrientation and didRotateFromInterfaceOrientation are never called.
It isn't presented in a navigation controller or anything. Not sure what else I need to do here to have rotation supported ONLY in the modally presented view controller.
After re-reading documentation, I believe my app has to support landscape orientations at the application (plist) level. In that case, I will have to change the supported orientations in my other view controllers to restrict it I guess.
When the application is in landscape mode (which I plan to force), displaying a modal view causes the parent view to rotate to portrait mode. If I set the return value of shouldAutoRotateToInterfaceOrientation to NO, the parent does not rotate, however the modal then slides in from the side and displays sideways. Below is the code that reveals the modal.
- (IBAction)loadExistingGame:(id)sender {
SavedGamesTableViewController *savedGames = [[SavedGamesTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
savedGames.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:savedGames animated:YES];
[savedGames release];
}
As per request here is the contents of the shouldAutoRotate method of the SavedGamesTableViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Override to allow orientations other than the default portrait orientation.
return YES;
}
Ok I figured out what needed to be done to fix it. The plist file that contains a list of the possible orientations needs to be limited to a single landscape view. The parent to the modal table view needs to have the shouldAutoRotateToInterfaceOrientation method return YES only if the orientation matches the only orientation in the plist file.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return interfaceOrientation = UIInterfaceOrientationLandscapeRight;
}
the modal viewcontroller should return NO for the same method.
Based on
When the application is in landscape
mode (which I plan to force),
displaying a modal view causes the
parent view to rotate to portrait
mode.
and
As per request here is the contents of
the shouldAutoRotate method of the
SavedGamesTableViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Override to allow orientations other than the default portrait orientation.
return YES;
}
So what you're saying is that the parent view controller is not yet set to force only using landscape orientation, and when you show a modal view that is set to allow all orientations, you're wondering why your parent view rotates to portrait when you rotate the device to portrait? I don't understand your question... aren't you saying that parent view controller is currently set to allow rotation to portrait? Isn't this behaviour exactly what should happen?
I had a similar problem when bringing up a modal mail view. Forcing the rotation didn't work for me, but calling presentModalViewController on the application's main view controller rather than a child view controller solved the issue.
I was seeing the same behavior; in my case the problem was I had implemented shouldAutorotateToInterfaceOrientation to return YES unconditionally for the parent view controller but NOT for the presented modal view controller. So I suspect Shaggy Frog's comment is the key: whether you want to force landscape mode or not, you need to make sure that the two view controllers' shouldAutorotateToInterfaceOrientation implementations agree or weirdness will ensue.
UIViewController *vc = /* create view controller */;
UINavigationController *nc = nil;
if (IOS_VERSION_LESS_THAN_6_0) {
nc = [[MyCustomNavigationControllerSupportingAllOrientations alloc] initWithRootViewController:vc];
} else {
nc = [[UINavigationController alloc] initWithRootViewController:vc];
}
[self.navigationController presentModalViewController:nc animated:YES];
On iOS6 I use a UINavigationController.
On pre-iOS6 I subclass UINavigationController, like this:
#interface MyCustomNavigationControllerSupportingAllOrientations : UINavigationController
#end
#implementation MyCustomNavigationControllerSupportingAllOrientations
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#end