-->

UIPopoverController not dismissed when opened from

2019-02-28 14:18发布

问题:

I have a problem dismissing a popover that was launched from the navigationItem of a UINavigationController. It seems that the navigation item which is inserted by the UINavigationController does not trigger dismissal of the UIPopoverController. Usually, when you tap outside a popover, it is dimissed. But when you tap on the navigation item, the popover is not dismissed. Worse, if you tap the button which triggers the popover, you'll get a second instance of the popover.

All of this is done using storyboards: - Create a view, embed it into a UINavigationView so it gets a navigationItem on the top. - Put a UIBarButtonItem into the navigationItem (left or right, doesn't matter for the initial view on the navigation stack). - Define another view, and drag a segue from the UIBarButtonItem to this view. - Set the segue to be popover.

Once the popover is open, I cannot dismiss it by tapping on the navigationItem. I cannot believe this "works as designed", so I suspect I missed out on somthing.

Although my goal is to program as little as possible (that's what storyboards are about, aren't they?), I thought about workarounds: The first workaround that came to my mind was to add a UITapGestureRecognizer to the navigationItem, which would dismiss the popover when it detected a tap on the navigationItem. Unfortunately, the navigationItem seems to not be UIVIew, so it lacks the addGestureRecognizer: method...

EDIT: Adding a UITapGesturerecognizer to self.navigationController.navigationBar is possible, but prevents any tap to reach the UIBarButtonItems on the navigationBar. Yes, I could have expected that.

Thanks a lot for help, nobi

回答1:

Here's the complete description for popovers in storyboards. Assuming your controller to appear in the popover is named MyPopupController, do this:

  1. Define a segue from the main scene to the MyPopupController scene, using style Popover.
  2. Add a property to MyPopupController.h

    @property (weak, nonatomic) UIPopoverController *popover;
    
  3. Synthesize the property in MyPopupController.m

    @synthesize popover = _popover
    
  4. In the prepareForSegue:sender: method of the main scene controller, set up the popoverproperty:

    UIStoryboardPopoverSegue *ps = (UIStoryboardPopoverSegue *)segue;
    MyPopupController *dc = ps.destinationViewController;
    dc.popover = ps.popoverController;
    
  5. In the viewWillAppear: method of MyPopupController, add the following code (don't forget [super viewWillAppear] as well):

    self.popover.passThroughViews = nil;
    

You should be done now.

Thanks to Robert for the basic idea - I just had to find the correct place because presentPopoverFromBarButtonItem is not used when using storyboards. Note that putting (5) into viewDidLoad does not work.

Have fun coding!



回答2:

https://stackoverflow.com/a/12874772/1455770

after presenting a popover from a bar button item, the popover has had its "passthroughViews" set to include the nav bar, so your taps don't register. Set the passthroughviews to nil straight after you present the popover. ie.

self.myPopoverController presentPopoverFromBarButtonItem:myBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
self.myPopoverController.passthroughViews = nil;// at this point the myPopoverController has had its pass through views set to include the whole nav bar. remove it.


回答3:

There might be a better solution than this, but why not only add the UITapGestureRecognizer to the navBar whenever the popover is open? Once you tap the button to open the popover, add the TapGestureRecogniser to the navBar. Once you dismiss the popover, remove the TapGestureRecogniser from the navBar.



回答4:

I've faced this problem recently and none of the solutions that I've seen around worked for me. Then after some research I've found out a way that works like a charm.

First you need to add the UIStoryboardPopoverSegue to your class.

@property (nonatomic, strong) UIStoryboardPopoverSegue *popoverSegue;

Synthesize it inside of the class implementation:

@synthesize popoverSegue;

Afterwards, inside of the function called by your button when pressed add the following code:

([[popoverSegue popoverController] isPopoverVisible]) ? 
            [self.popoverSegue.popoverController dismissPopoverAnimated: YES] :
            [self performSegueWithIdentifier: @"popSegue" sender:nil];

Now you are almost ready. inside of the method - (void) prepareForSegue:(UIStoryboard)segue sender:(id)sender add the following code:

if([[segue identifier] isEqualToString:@"popSegue"]){
    self.popoverSegue = (UIStoryboardPopoverSegue*) segue;
    if(viewPopoverController == nil){
        viewController = [[UIViewController alloc] init];
        viewPopoverController = [[UIPopoverController alloc] initWithContentViewController:viewController];
    }
}

Now every time you press the button it will either show or dismiss your window, the window will also be dismissed if you press outside of the popover. I hope it can help someone.