As the answers to that other question point out, once you hit prepareForSegue
, it's too late to cancel it. You need to prevent the segue from being invoked the second time at all, rather than trying to stop it half way through the segue process. You can accomplish this in a variety of ways, depending upon the desired behavior:
First, if you'd like a second click on the button to dismiss the popover, you could (a) remove the segue; and (b) programmatically either present the popover or dismiss it as appropriate:
- remove the popover segue from storyboard;
- give your popover view a unique identifier so you can instantiate the controller via code;
- make sure you have some
IBOutlet
or other reference to your navigation bar button (in my code sample below, it's myNavBarButton
) so that you can tell your popover to be presented from that button;
- you might want to also change the simulated metrics for the popover view to be "freeform", because if you leave it to "inferred", Interface Builder will assume you want it to be full screen and will repeatedly resize it for you;
- define an ivar for your popover, e.g.
UIPopoverController *_popover;
;
- define and link
IBAction
for your navigation bar button that does the work of dismissing or presenting of the popover as appropriate; and
- make sure to define view controller with the button to be a
UIPopoverControllerDelegate
so that it can receive and handle the popoverControllerDidDismissPopover
event.
Thus, the ARC solution might look like:
@interface MyViewController ()
{
UIPopoverController *_popover;
}
@end
@implementation MyViewController
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
_popover = nil;
}
- (IBAction)navbarButtonClicked:(id)sender
{
if (_popover)
{
// If the popover already is present, dismiss it
// I'm dismissing the popover here, but if you wanted to do something else,
// e.g. like do nothing, you could replace these two lines with whatever
// behavior you want if the user clicked on the button again.
[_popover dismissPopoverAnimated:YES];
_popover = nil;
}
else
{
// If the popover doesn't exist yet, let's create and present it
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"gesturePopover"];
_popover = [[UIPopoverController alloc] initWithContentViewController:controller];
[_popover presentPopoverFromBarButtonItem:self.myNavBarButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}
// the rest of your main view controller's implementation
@end
Second, you could alternatively just disable the navigation bar button, so you don't get popovers on top of popovers. You'll also have a nice visual indication that it's disabled. So bottom line, you can disable the button in prepareForSegue
before you perform the segue and then re-enable it when the popover is dismissed. You will need to define an identifier for your segue in Interface Builder and you'll need an outlet/reference to your navigation bar button so that you can enable and disable it. It would then look like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"mySegueIdentifier"])
{
UIPopoverController *popover = [(UIStoryboardPopoverSegue *)segue popoverController];
popover.delegate = self;
self.myNavBarButton.enabled = FALSE;
}
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
self.myNavBarButton.enabled = TRUE;
}