iOS 8 UIActionSheet ignores view controller supportedInterfaceOrientations and shouldAutorotate
Asked Answered
L

5

25

I have an application that is configured via the plist file to support portrait, landscape left, and landscape right orientations (i.e. UISupportedInterfaceOrientations is set to UIInterfaceOrientationPortrait, UIInterfaceOrientationLandscapeLeft, and UIInterfaceOrientationLandscapeRight). But I've limited the supported orientations to just portrait inside of a view controller. If I present a UIActionSheet on the view controller's view and then rotate the device, the UIActionSheet rotates to the new orientation. This only happens on devices running iOS 8 (GM Seed).

I would like the UIActionSheet to follow the rules of the containing view controller and not rotate. Thoughts?

I don't want this to happen: screenshot

UIViewController Sample Code:

- (IBAction)onTouch:(id)sender
{
    UIActionSheet * actionSheet = [[UIActionSheet alloc] initWithTitle:@"hi"
                                                       delegate:nil
                                              cancelButtonTitle:@"Cancel"
                                         destructiveButtonTitle:nil
                                              otherButtonTitles:@"Open", nil];

    [actionSheet showInView:self.view];
}

#pragma mark - Rotation methods

- (BOOL)shouldAutorotate
{
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}
Ladder answered 13/9, 2014 at 2:1 Comment(0)
L
5

Was informed that UIAlertController replaces UIActionSheet in iOS 8.

"The new UIAlertController class replaces the UIActionSheet and UIAlertView classes as the preferred way to display alerts in your app."

https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"hi"
                                                                          message:nil
                                                                   preferredStyle:UIAlertControllerStyleActionSheet];

[alertController addAction:[UIAlertAction actionWithTitle:@"Open"
                                                  style:UIAlertActionStyleDefault
                                                handler:^(UIAlertAction *action) {
                                                   // open something
                                                }]];

[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
                                                  style:UIAlertActionStyleCancel
                                                handler:nil]];

[self presentViewController:alertController animated:YES completion:nil];
Ladder answered 15/9, 2014 at 14:3 Comment(4)
Sure, there is a new "preferred" way, but why change/break old working functionality just because a new way is introduced? Is this new way the only way to get the original behavior?Joppa
No, because iOS7 users still have to deal with the old alert/sheets. Clearly a bug that need fixing.Foreshore
On iOS7 the old UIActionSheet does not exhibit the rotation issue. Use UIActionSheet on iOS7 and older. Use UIAlertController on iOS8 and newer. Check for NSClassFromString(@"UIAlertController") to determine which code to use.Ladder
@ChitraKhatri I add an example of how to use UIAlertController to the post above.Ladder
L
21

Here is my workaround based on the solution from Busrod with some modifications because I had some problems in UIActionSheet using his workaround:

-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    UIViewController *presentedViewController = window.rootViewController.presentedViewController;
    if (presentedViewController) {
        if ([presentedViewController isKindOfClass:[UIActivityViewController class]] || [presentedViewController isKindOfClass:[UIAlertController class]]) {
            return UIInterfaceOrientationMaskPortrait;
        }
    }
    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
Lynellelynett answered 25/9, 2014 at 8:12 Comment(4)
Yeah, this would work. Thank you! I opted to use the new UIAlertController if available. The use of blocks are quite nice and I'll soon be dropping iOS 7 support.Ladder
This fails with portrait upside down.Hygrostat
if you want to support upside down, return UIInterfaceOrientationMaskAll instead.Eck
Had same issue for SLComposeViewController, can be also added inside if statementZephaniah
J
11

I had a similar problem with iOS 8 and found a workaround that may help if you want to stick with UIAlertView and UIActionSheet for now, instead of migrating to UIAlertController.

My app allows Landscape and Portrait, but only on select views. Most of the time it only allows Portrait. I want UIAlertView and UIActionSheet to appear only Portrait (because the views they are on only allow Portrait).

Unfortunately, with iOS 8 the alerts and action sheets now rotate, ignoring the shouldAutorotate method of the window's root view controller. The view and status bar underneath the alert stay visible in Portrait even if the alert rotates. If the alert takes an input, the keyboard also stays Portrait, so it is really unattractive.

I was about to give up, but finally found something that works, even if it is not ideal. Put this in your app delegate, and adapt as needed. I'm looking forward to a better solution (probably just using the new classes). String comparison here is obviously lame, and this will override whatever you have set as supported orientation in Info.plist. At least it is something to go on...

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    NSString *viewControllerClassName = [NSString stringWithUTF8String:object_getClassName(window.rootViewController)];
    if ([viewControllerClassName isEqualToString:@"_UIAlertShimPresentingViewController"])   {
        return UIInterfaceOrientationMaskPortrait;
    }
    else {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
    }
}

Read more about this delegate here:

https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:supportedInterfaceOrientationsForWindow:

Joppa answered 19/9, 2014 at 15:51 Comment(2)
Will this result in an App Store rejection since it is using a private class?Glarus
If you're worried about "_UI...", try user3750435's variation below. "Using" the class name to determine when not to rotate isn't the safest thing, but shouldn't be a violation as it isn't calling any method/property of the class. As another alternative, you could match the window to your own known window. My use is enterprise only so no risk of rejection.Joppa
L
5

Was informed that UIAlertController replaces UIActionSheet in iOS 8.

"The new UIAlertController class replaces the UIActionSheet and UIAlertView classes as the preferred way to display alerts in your app."

https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"hi"
                                                                          message:nil
                                                                   preferredStyle:UIAlertControllerStyleActionSheet];

[alertController addAction:[UIAlertAction actionWithTitle:@"Open"
                                                  style:UIAlertActionStyleDefault
                                                handler:^(UIAlertAction *action) {
                                                   // open something
                                                }]];

[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
                                                  style:UIAlertActionStyleCancel
                                                handler:nil]];

[self presentViewController:alertController animated:YES completion:nil];
Ladder answered 15/9, 2014 at 14:3 Comment(4)
Sure, there is a new "preferred" way, but why change/break old working functionality just because a new way is introduced? Is this new way the only way to get the original behavior?Joppa
No, because iOS7 users still have to deal with the old alert/sheets. Clearly a bug that need fixing.Foreshore
On iOS7 the old UIActionSheet does not exhibit the rotation issue. Use UIActionSheet on iOS7 and older. Use UIAlertController on iOS8 and newer. Check for NSClassFromString(@"UIAlertController") to determine which code to use.Ladder
@ChitraKhatri I add an example of how to use UIAlertController to the post above.Ladder
N
0

Adding to @user3750435 's answer

Since the OP has defined the rotation masks he/she wants in the Info.plist, we do not need to redefine the masks here. UIApplication can return us those masks via supportedInterfaceOrientationsForWindow:. So, the method can be made simpler like this:

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    UIViewController *presentedViewController = window.rootViewController.presentedViewController;
    if (presentedViewController) {
        if ([presentedViewController isKindOfClass:[UIActivityViewController class]] || [presentedViewController isKindOfClass:[UIAlertController class]]) {
            return UIInterfaceOrientationMaskPortrait;
        }
    }
    return [application supportedInterfaceOrientationsForWindow:window];
}
Nigeria answered 3/3, 2015 at 6:33 Comment(0)
M
0

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {

UIViewController *presentedViewController = window.rootViewController.presentedViewController;
if (presentedViewController) {
    if ([presentedViewController isKindOfClass:[UIActivityViewController class]] || [presentedViewController isKindOfClass:[UIAlertController class]]) {
        return UIInterfaceOrientationMaskPortrait;
    }
}
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;

}

thanks this worked for me

Myotonia answered 25/8, 2015 at 12:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.