Search code examples
iosobjective-cxcodepopover

UIPopoverPresentationControllerDelegate calls on iPhone


I'm attempting a project using the Xcode (version 9.1 9B55) Master-Detail template, using one Storyboard for both iPhone and iPad.

I want to use the built in popover segue, which ideally will show a popover when the size class is appropriate, or a modal view controller when the size class is compact (iPhone in portrait).

What I am finding is that it works just fine for iPad, but when I run it on an iPhone, you can't touch outside the popover to dismiss as I would expect.

When on iPad, popoverPresentationControllerDidDismissPopover is called and all is well.

When on iPhone, the popoverPresentationControllerDidDismissPopover never gets called and you cannot dismiss the popover.

To reproduce, I did this:

  • Create new Master-Detail App

  • New - File. Cocoa Touch Class, called MyPopoverViewController

In Storyboard:

Create new view controller, change class to MyPopoverViewController.

On MasterViewController, add bar button item "Popover". Control-drag from this to MyPopoverViewController. Set segue Kind to "Present As Popover". Set Identifier to "thePopover".

In MasterViewController.h, add UIPopoverPresentationControllerDelegate:

@interface MasterViewController : UITableViewController <UIPopoverPresentationControllerDelegate>

In MasterViewController.m:

#import "MyPopoverViewController.h"

In viewDidLoad, comment out two lines which create the "Add Button".

In prepareForSegue:

} else if ([[segue identifier] isEqualToString:@"thePopover"]) {
    NSLog(@"MVC prepareForSegue thePopover");
    MyPopoverViewController *myPopoverController = segue.destinationViewController;
    myPopoverController.popoverPresentationController.delegate = self;
}

Add three UIPopoverPresentationControllerDelegate delegate methods:

- (void) prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController {
    NSLog(@"MVC prepareForPopoverPresentation");
}

- (void) popoverPresentationControllerDidDismissPopover:(UIPopoverPresentationController *)popoverPresentationController {
    NSLog(@"MVC popoverPresentationControllerDidDismissPopover");

}

- (BOOL) popoverPresentationControllerShouldDismissPopover:(UIPopoverPresentationController *)popoverPresentationController {
    NSLog(@"MVC popoverPresentationControllerShouldDismissPopover");
    return TRUE;

}

I tried this also, but all it does is force popover in portrait mode (which I don't want); doesn't change the lack of popover delegate calls and doesn't allow us to dismiss popover:

 -(UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone;
 }

I'm hoping there is something simple I'm missing here. I have uploaded a sample project here, which is exactly what I've described above:

https://github.com/johnstewart/MasterDetailPopoverTestProject

How do I allow iPhone to also dismiss popovers by touching outside the popover?


Solution

  • If I understand your question correctly your problem appears on iPhone 8 Plus in landscape mode.

    In this situation, the presented popover actually is not a popover but a normal presented view. Visually it looks like a sheet that appears from the botton of the screen. In order to close such a view, you have to add your own button to do this.

    If you want to show a real popover, you must implement:

    adaptivePresentationStyleForPresentationController:traitCollection:
    

    to return UIModalPresentationNone. Note the additional parameter traitcollection:. UIAdaptivePresentationControllerDelegate contains two similar methods. In your project you already implemented the method:

    adaptivePresentationStyleForPresentationController:
    

    Change this to the former method and everything should work.