iPad modal controller is dismissed after rotation

I am presenting a modal view using a storyboard segue set as Form Sheet. The problem is, when I rotate the iPad after this view is displayed, the view is removed from the view/dismissed.

I have no idea why. It only seems to occur when starting in Portrait then rotating to Landscape.

If I start in Landscape then show the view then rotate it stays on the screen fine.

Any ideas?

EDIT ----

It also seems that full screen modal views are also dismissed after rotation!

There's nothing special going on in the presentation code, this is a full screen modal:

EditViewController *editView = [self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"editViewController"];
editView.delegate = self;
editView.image = image;
editView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

[self presentViewController:editView animated:YES completion:nil];

This happens on both iOS 6 and iOS 7

EDIT 2 ---- Forgot to mention, i'm presenting the modal from the left/master view controller of a UISplitViewController

Answers


late, but what it worked for me was just before

[self presentViewController:aController animated:YES completion:nil];

dismiss the master controller, adding this lines

[self.splitViewController setPreferredDisplayMode:UISplitViewControllerDisplayModePrimaryHidden];
[self.splitViewController setPreferredDisplayMode:UISplitViewControllerDisplayModeAutomatic];

and then present your controller


Get rid of: editView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; That will solve the issue you are experiencing. Modal view controllers presented on iPad as a Form Sheet do not rotate correctly using that transition style.


its really hard to get the cause how and why this is happening as i found that this also happen with UIPopover also as when you rotate it UIPopover hide because ???

So if you want to keep your view then just call again your controller after rotation will do fine user experience


This is not a bug its a limitation on UISplitViewController. The problem exists when the masterViewController (which is a UIPopoverController) is able to be dismissed. Heres how it works with the assumption that your app does allow the masterViewController to be dismissed in portrait and does not allow in landscape.

In portrait while the masterViewController is visible, if you were to present a modal from a viewController in the masterViewController and then rotate to landscape, the modal would disappear in iOS7 and the app would not rotate in iOS8. iOS8 introduces a condition to prevent the bad experience of iOS7. iOS7 losses the modal in the process of moving the masterViewController from the popoverController to a contained viewController in the splitViewController.

The modal needs to be presented from the splitViewController and not from the masterViewController. The only problem with this is the modal gets presented below the masterViewController in portrait. My solution is to dismiss the masterViewController and then present the modal.

There are several ways to achieve this result depending on how complex your code needs to be. Here's how I do this in my app.

I first subclass UISplitViewController in order to have a reference to the popoverController. I use delegate forwarding in order to access the delegate methods internally and externally. Heres the .h

// MainSplitViewController.h

#import <UIKit/UIKit.h>

@interface MainSplitViewController : UISplitViewController
@property (nonatomic, weak, readonly) UIPopoverController* primaryColumnController;
@end

And the .m

// MainSplitViewController.m

#import "MainSplitViewController.h"

@interface MainSplitViewController () <UISplitViewControllerDelegate>
@property (nonatomic, weak) id<UISplitViewControllerDelegate> externalDelegate;
@property (nonatomic, weak) UIPopoverController* primaryColumnController;
@end

@implementation MainSplitViewController

- (instancetype)init {
    self = [super init];
    if (self) {
        self.delegate = self;
    }
    return self;
}


#pragma mark - Split View Controller Delegate

- (void)splitViewController:(UISplitViewController *)svc popoverController:(UIPopoverController *)pc willPresentViewController:(UIViewController *)aViewController {
    self.primaryColumnController = pc;

    if ([(id)self.externalDelegate respondsToSelector:_cmd]) {
        [self.externalDelegate splitViewController:svc popoverController:pc willPresentViewController:aViewController];
    }
}


#pragma mark - Delegate Forwarder

- (void)setDelegate:(id<UISplitViewControllerDelegate>)delegate {
    [super setDelegate:nil];
    self.externalDelegate = (delegate != self) ? delegate : nil;
    [super setDelegate:delegate ? self : nil];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    id delegate = self.externalDelegate;
    return [super respondsToSelector:aSelector] || [delegate respondsToSelector:aSelector];
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    id delegate = self.externalDelegate;
    return [delegate respondsToSelector:aSelector] ? delegate : [super forwardingTargetForSelector:aSelector];
}

@end

Next I create a class extension on UIViewController

// UIViewController+Popover.h

#import <UIKit/UIKit.h>

@interface UIViewController (Popover)
- (UIViewController *)popoverPresentingViewController;
@end

And the .m

// UIViewController+Popover.m

#import "UIViewController+Popover.h"
#import "MainSplitViewController.h"

@implementation UIViewController (Popover)

- (UIViewController *)popoverPresentingViewController {
    UIViewController* viewController = self;

    if ([self.splitViewController isKindOfClass:[MainSplitViewController class]]) {
        viewController = self.splitViewController;

        MainSplitViewController* mainSplitViewController = (MainSplitViewController *)self.splitViewController;

        if (mainSplitViewController.primaryColumnController.popoverVisible) {
            [mainSplitViewController.primaryColumnController dismissPopoverAnimated:YES];
        }
    }

    return viewController;
}

@end

Now where ever you present the modal, instead of calling [self presentViewController: ... call [self.popoverPresentingViewController presentViewController: ...]. Remember to import UIViewController+Popover.h


your question came closest to my bug, On returning from modalView the parentView will switch to orientation in which the application was opened. Visually it appears that the modal view is rotated and then returns.

I solved it by removing the modal view altogether, and using

[self.navigationController pushViewController: <the View(not modal now)>]

instead of using-

[self presentViewController:<Modal View>]

I think this is because the navigation controller doesn't own the Modal View, hence it reloads - when returning from the modal view - to incorrect orientation


Problem:

When presenting a view controller modally, it gets dismissed on rotation.

Approach:

  • Set the UISplitViewControllerDelegate
  • Use the UISplitViewControllerDelegate methods
  • Hold a reference to your modal view controller in an instance variable
  • Check if your modal view controller's presenting view controller exists.
  • If it exists, nothing needs to be done, else just present without any animation.

UISplitViewControllerDelegate methods:

func primaryViewController(forCollapsing splitViewController: UISplitViewController) -> UIViewController? {

    if let someModalViewController = someModalViewController, 
        someModalViewController.presentingViewController == nil {

        let masterViewController = viewControllers.first

        masterViewController?.present(someModalViewController,
                                      animated: false) {

        }
    }

    return nil
}

func primaryViewController(forExpanding splitViewController: UISplitViewController) -> UIViewController? {

    if let someModalViewController = someModalViewController, 
        someModalViewController.presentingViewController == nil {

        let masterViewController = viewControllers.first

        masterViewController?.present(someModalViewController,
                                      animated: false) {

        }
    }

    return nil
}

Note:

  • UISplitViewControllerDelegate has quite a methods, it can be daunting initially, if you spend some time experimenting, you can achieve what you want.
  • It has fine grained access.

I'm very late but try this. It works for me.

[self.splitViewController presentViewController:editView animated:YES completion:nil];

Need Your Help

JDialog with minimize button

java swing modal-dialog jdialog

Is it possible to have a minimize and maximize button for a non-modal(modal=false) JDialog.I know JFrame is the ideal solution for this but this change has to be made in an existing code and its l...

Inserting records from another query

sql sql-server-2008

I am beginner user and have some basic knowledge of the SQL. I have to write a query to select the records from a table and insert into another table. My TABLE1 has almost 180+ columns and TABLE2 has