Unwind segue doesn't dismiss adaptive popover presentation when not modal

Update for iOS 9 beta: Apple may have fixed this for iOS 9. If you work(ed) around this issue for iOS 8, make sure it also works correctly on iOS 9.

In storyboard, I’ve created a popover presentation segue to present a navigation and view controller from a button, as well as creating an unwind segue.

  • Is it possible to run iOS simulator on a Mac without Xcode installed?
  • How to get notified when scrollToRowAtIndexPath finishes animating
  • Cannot add right bar button in navigation bar in storyboards iOS8
  • Phasset + AfNetworking Upload Multiple Videos
  • Xcode6:Embedded binary is not signed with the same certificate as the parent app
  • Limit length of multiple UITextFields in Swift 2
  • In portrait orientation, the modal (fullscreen) presentation is unwound/dismissed, as expected.

    In landscape orientation, the unwind segue also gets called, however the popover presentation is not automatically dismissed.

    Did I miss hooking something up? Do I have to dismiss the popover presentation myself?

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)__unused sender
    {
        if ([[segue identifier] isEqualToString:@"showSelectBookChapter"])
        {
            UINavigationController *navigationController = segue.destinationViewController;
    
            if ([navigationController.topViewController isKindOfClass:[BIBLESelectViewController class]])
            {
                BIBLESelectViewController *selectViewController = (BIBLESelectViewController *)navigationController.topViewController;
                selectViewController.initialBookChapterVerse = self.bookChapterVerse;
            }
        }
    }
    
    - (IBAction)unwindToBIBLEChapterViewController:(UIStoryboardSegue *)segue
    {
        if ([segue.identifier isEqualToString:@"unwindToBIBLEChapterViewController"]) {
            if ([segue.sourceViewController isKindOfClass:[BIBLESelectViewController class]])
            {
                BIBLESelectViewController *sourceViewController = (BIBLESelectViewController *)segue.sourceViewController;
                self.bookChapterVerse = sourceViewController.selectedBookChapterVerse;
                [self.tableView reloadData];
    
            }
        }
    }
    

    Storyboard scenes and segues
    Update:
    After looking at gabbler’s sample code, I’ve narrowed the problem down to popover dismissing fine in a Single View Application, but not in a Master-Detail Application.

    Update 2:
    Here’s what the hierarchy looks like (omitting navigation controllers for simplicity’s sake), in answer to the question Luis asked:

    • Split view controller
      • Master view controller
      • Detail view controller
        • Chapter view controller (modal page sheet)
          • Select view controller (the problematic popover that unwinds to chapter view controller, but doesn’t dismiss)

    As I mentioned in the previous update, I created an new master/detail template, and simply presented a popover directly from (a button in) the detail view. It won’t dismiss.

    4 Solutions Collect From Internet About “Unwind segue doesn't dismiss adaptive popover presentation when not modal”

    I ran into this problem too. I present a View Controller modally (as a form sheet), from the Master View Controller (UISplitViewController). The problem only occurred on the iPad (probably the iPhone 6+ in landscape mode too, but I didn’t check it). I ended up doing the following in my unwind action method (using Swift), and it works good.

    if !segue.sourceViewController.isBeingDismissed() {
        segue.sourceViewController.dismissViewControllerAnimated(true, completion: nil)
    }
    

    If you segue as a popover from a view controller embedded in a navigation controller, the corresponding unwind fails to dismiss the popover.

    It’s a bug in -[UINavigationController segueForUnwindingToViewController:fromViewController:identifier]. The embedding navigation controller is supposed to supply a segue that will dismiss the popover but it doesn’t. The fix then is to override this and supply a working segue, which we can get from the embedded view controller.

    Here’s a partial solution that will only handle unwinding to the top view controller of the navigation stack:

    @implementation MyNavigationController
    
    - (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
                                          fromViewController:(UIViewController *)fromViewController
                                                  identifier:(NSString *)identifier
    {
      if (toViewController == self.topViewController && fromViewController.presentingViewController == self)
        return [toViewController segueForUnwindingToViewController:toViewController
                                                fromViewController:fromViewController
                                                        identifier:identifier];
      else
        return [super segueForUnwindingToViewController:toViewController
                                     fromViewController:fromViewController
                                             identifier:identifier];
    }
    
    @end
    

    It works on iOS 8 for both landscape/portrait iPad and landscape/portrait iPhone. The logic should be robust enough to survive on iOS 9.

    It is/must be a behavior of the popOver segue, in normal situations or regularly we need that the popOver keeps in view, if the segue show something important is annoying that we lost that information just because we rotate the device, I guess that that is the reason of that native behavior. So if we want for it to dismiss automaticly we have to make that behaivor by our own, this works:

    in the method - (void)viewDidLoadin the detailViewController.m add this:

    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter]
     addObserver:self selector:@selector(orientationChanged:)
     name:UIDeviceOrientationDidChangeNotification
     object:[UIDevice currentDevice]];
    

    then create this method:

    - (void) orientationChanged:(NSNotification *)note{
    UIDevice * device = note.object;
    //CGRect rect = [[self view] frame];
    switch(device.orientation)
    {
        default:
            [self dismissViewControllerAnimated:YES completion:nil];
        break;    }}
    

    You said that in a single view happens what you want, but I’ve never seen that behavior when I used popOvers.

    mbeaty’s fix is great but as others have pointed out, this bug seems to know be fixed in iOS 9 and it also doesn’t work well for universal device design. I have adapted his answer to handle both situations. Here is the code:

    @IBAction func yourUnwindSegue(segue: UIStoryboardSegue) {
        if #available(iOS 9, *) {
            return  // bug fixed in iOS 9, just return and let it work correctly
        }
        // do the fix for iOS 8 bug
    
        // access your SplitViewController somehow, this is one example
        let appDelegate  = UIApplication.sharedApplication().delegate as! AppDelegate
        let splitVC = appDelegate.window!.rootViewController as! YourSplitViewController
    
        // if the source isn't being dismissed and the splitView isn't
        //    collapsed (ie both windows are showing), do the hack to
        //    force it to dismiss
        if !segue.sourceViewController.isBeingDismissed() && splitVC.collapsed == false {
            segue.sourceViewController.dismissViewControllerAnimated(true, completion: nil)
        }
    }
    

    This first checks if iOS 9 is running and just exit as the bug seems to be fixed. This will prevent the multiple views getting dismissed issue. Also to make sure this fix is only done when the splitView is showing two windows (to make it happen only on iPads and iPhone 6 Plus in landscape as well as future devices) I added the check to make sure it is not collapsed.

    I have not exhaustively check this but it seems to work. Also not that my app is set for a min of iOS 7, I don’t know if this bug existed then so you may need to look into that if you support below iOS 8.