6

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.

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.

  • So in portrait it is not working? I can't reproduce that. – gabbler Feb 02 '15 at 06:56
  • It works correctly in portrait. In landscape (on the iPad or iPhone 6 Plus), it's not dismissing because it's a popover instead of modal. –  Feb 02 '15 at 06:58
  • Works for me on iPad and iPhone 6 Plus in landscape. – gabbler Feb 02 '15 at 07:04
  • Do you have any sample code you can share, so I can try to figure out what's different? –  Feb 02 '15 at 07:06
  • Please see https://www.dropbox.com/s/g0148ffwmb4431y/LandscapeUnwind.zip?dl=0 – gabbler Feb 02 '15 at 07:10
  • It is working fine for the single view controller template. But on the detail side in the Master/Detail template, the popover doesn't dismiss. Either an SDK bug, or something additional needs to be done for the (adaptive UI) storyboard. –  Feb 02 '15 at 21:06
  • It is a bug, if the segue start from MainViewController, you have to dismiss it yourself, if it start from ContentViewController, you can subclass SplitViewController and implement the unwind segue action in the subclass. – gabbler Feb 09 '15 at 05:38
  • The unwind segue action happens. But it appears that I have to dismiss the popover myself. I added `dismissViewController` to the unwind segue. –  Feb 09 '15 at 05:55
  • If you segue from contentViewController of split view controller, you don't have to dismiss it, it will be automatically dismissed. – gabbler Feb 09 '15 at 05:58
  • Hmm, that's interesting, but I am passing details back to the presenting view controller. If I have to subclass `UISplitViewController`, it would have to pass those details on to the right view controller. I think it's clearer just to manually dismiss it. –  Feb 09 '15 at 06:07
  • But if you want to post your code as an answer, I can mark it as accepted. –  Feb 09 '15 at 06:13
  • The code I found worked is here, do svn co https://github.com/mattneub/Programming-iOS-Book-Examples/trunk/iOS6bookExamples/ch19p642containerViewControllerStoryboard3Bug/ – gabbler Feb 09 '15 at 06:26

4 Answers4

11

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)
}
mbeaty
  • 1,419
  • 11
  • 8
  • I appreciate your suggestion. It looks like a safe way to handle the issue. Thanks! –  Mar 01 '15 at 20:08
  • This solution works in landscape mode, but in portrait mode, it rewinds all the way back to the list. – hebinda May 08 '15 at 13:41
  • It works for my situation. I think the conclusion is probably that it depends on how your storyboard is set up and what type of modal you are using. – mbeaty May 10 '15 at 20:31
  • This works. However in my case I had to additionally unwrap sourceViewController (`AnyObject`) and check that it's actually a `UIViewcontroller`. – Tomas Camin May 27 '15 at 07:25
  • 1
    This workaround stopped working for iOS 9 beta. Apple apparently fixed the initial issue, but this check doesn't catch that it's being dismissed, resulting in 2 dismissals. –  Jun 29 '15 at 17:52
  • Good to know. Guess I'll have some code to fix. Thanks for the heads up! – mbeaty Jun 30 '15 at 19:17
  • So this issue is fixed in iOS 9? – Ace Green Jul 02 '15 at 17:15
  • I have the EXACT same segue in my other app and it works. I don't understand this. Also it seems the unwind doesn't work on any iPad simulator and it works on my iPad mini device but not on an iPad air device. – Ace Green Jul 02 '15 at 17:45
  • I have added answer that is an expansion of mbeaty's answer that handles issues with iOS 9 and universal apps. – jeffjv Sep 23 '16 at 06:48
4

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.

Glen Low
  • 4,111
  • 1
  • 28
  • 34
1

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.

Luis Mejías
  • 183
  • 1
  • 10
  • Not using the detail view controller as a popover. I've updated my question to show the scene hierarchy. The apparent problem is that a popover won't automatically dismiss when presented from the detail view controller (but it will dismiss when presented from the non-splitview single view template). –  Feb 05 '15 at 20:06
  • Yes, I'm using the Master-Detail Application template. If you put a button in the detail view, and present a popover from that button, it apparently won't automatically dismiss. –  Feb 05 '15 at 20:12
  • I understand that I can manually dismiss the popover (and I am dismissing it in the unwind segue as a workaround for now). I'm not asking "How do I dismiss it?" I'm asking "Why isn't it automatically dismissed?" –  Feb 05 '15 at 21:01
  • You never asked that, just: Did I miss hooking something up? Do I have to dismiss the popover presentation myself? It 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. – Luis Mejías Feb 05 '15 at 21:03
  • Yes. I did not expect needing to manually dismiss it. I expected it to dismiss automatically, as it does for a single view application. But if someone answers, "There's nothing wrong with the code. It's not a bug. You must dismiss it manually because ," I've learned something about the SDK I didn't know. –  Feb 05 '15 at 21:08
  • It's not meant to be dismissed by rotation. It transitions between popover and full-screen because of the size class change. It's doing the right thing for auto-rotation. It's not doing what's expected -- dismiss -- when the unwind segue fires. –  Feb 05 '15 at 21:12
  • yes and that happens for the reason that I said, it disappears and appears how you said but do not dismiss how I said before. But, why do you want that behavior? – Luis Mejías Feb 05 '15 at 21:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70338/discussion-between-petahchristian-and-luis-mejias). –  Feb 05 '15 at 21:20
0

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.

jeffjv
  • 2,781
  • 1
  • 19
  • 27