33
votes

With iPad with iOS6, we have this bug where a modal view controller will expand to full screen, even if it is told to be using "form sheet" presentation style. But, this happens only if there are two modals, a parent one and its child.

So this is how the first modal is created and presented:

UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:controller] autorelease];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[parentController presentModalViewController:navigationController animated:YES];
// parentController is my application's root controller

This is how the child modal is created and presented:

UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:controller] autorelease];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[parentController presentModalViewController:navigationController animated:YES];
// parentController is the navigationController from above

So when rotating from landscape to portrait, the parent modal will expand to full screen and remain that way even if we rotate back to landscape.

When we have the parent modal all by itself (no child modal), then it works as expected, which is that it remains in form sheet style.

Note that this happens on iOS6 only (device and simulator) and doesn't happen on iOS 5 (simulator and reported to work by testers).

So far, I have tried the following without success:

  • setting wantsFullScreenLayout to NO
  • forcing wantsFullScreenLayout to always return NO by overriding it
  • Making certain my controllers inside the navigation controller also specify UIModalPresentationFormSheet
  • implementing preferredInterfaceOrientationForPresentation
  • upgrade to iOS 6.0.1

Thanks!


UPDATE: So, I adapted the response from the Apple Developer Forums (https://devforums.apple.com/message/748486#748486) so that it works with multiple nested modal.

- (BOOL) needNestedModalHack {
    return [UIDevice currentDevice].systemVersion.floatValue >= 6;
}

- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                          duration:(NSTimeInterval)duration {

    // We are the top modal, make to sure that parent modals use our size
    if (self.needNestedModalHack && self.presentedViewController == nil && self.presentingViewController) {
        for (UIViewController* parent = self.presentingViewController;
             parent.presentingViewController;
             parent = parent.presentingViewController) {
            parent.view.superview.frame = parent.presentedViewController.view.superview.frame;
        }
    }

    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                 duration:(NSTimeInterval)duration {
    // We are the top modal, make to sure that parent modals are hidden during transition
    if (self.needNestedModalHack && self.presentedViewController == nil && self.presentingViewController) {
        for (UIViewController* parent = self.presentingViewController;
             parent.presentingViewController;
             parent = parent.presentingViewController) {
            parent.view.superview.hidden = YES;
        }
    }

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    // We are the top modal, make to sure that parent modals are shown after animation
    if (self.needNestedModalHack && self.presentedViewController == nil && self.presentingViewController) {
        for (UIViewController* parent = self.presentingViewController;
             parent.presentingViewController;
             parent = parent.presentingViewController) {
            parent.view.superview.hidden = NO;
        }
    }

    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
3
Somebody pointed me to the apple developer forums and I found this: devforums.apple.com/message/748486GenesisST
Presenting multiple modals clearly breaks the way Apple say you should be doing things. If you end up doing things like this which go against Apple' advice then expect problems like this. Also consider you probably have a very bad design. If you want to show multiple viewControllers like this you should be using containment or a navigationController in a single modal presentation. What are you going to do when this breaks again in iOS 6.3?ader
@Ade: This document mentions that it is possible to chain modal view controllers: developer.apple.com/library/ios/#featuredarticles/…GenesisST
Thanks for pointing me at this, for what it's worth I thought you were trying to present more than one modal viewController from a single viewController parent (not chaining). I have always personally had the viewControllers in a navigation controller. Feel like an ass I do ;)ader

3 Answers

7
votes

Not sure if this should be considered as a bug and I'm curious what iOS 7 will bring, but the current workaround for this issue is to set modalPresentationStyle to UIModalPresentationCurrentContext for the child-viewController.

Set modalPresentationStyle = UIModalPresentationCurrentContext

This makes the child still beeing presented as FormSheet but prevents the parent from beeing resized to fullscreen on rotation.

Dirk

0
votes

I can see 2 problems here.

1) in iOS 6 the method presentModalViewController:animated: is deprecated, try using presentViewController:animated:completion: (despite this might not help, you still may want to do it)

2) In iOS 6 somehow appeared that container controllers (such as UINavigationController) don't resend the autorotate messages to their children. Try subclassing the UINavigationController and redefine the corresponding autorotation methods to be sent to all of the children. This might help.

-1
votes

You need to instanciate your navigation controller after your main view. So that you will be able to manage rotation in each view.

If your AppDelegate RootViewController is a navigation controller, you will not be able to manage rotation with native functions.