49

I have a problem with a popover. If I tap on a cell I will load a popover to select more details. Everything works fine, but when I press my cell again I receive every time the following message:

Warning: Attempt to present ModalTableViewController... on MainTableViewController... which is already presenting (null)

If I tap on another cell I will not get this Warning. Only if a tap the same row again.

I tried lots of things but I am not able to solve this problem. I load my popover with like this:

var popover: UIPopoverController!
var popoverContent: ModalTableViewController!

and on my cell tap:

popoverContent = self.storyboard.instantiateViewControllerWithIdentifier("ModalTableViewController") as ModalTableViewController

popoverContent.selectedQuestionID = indexPath!.row               
popover = UIPopoverController(contentViewController: popoverContent)
popover.delegate = self

popover.presentPopoverFromRect(currentCell.LabelCellTitle.frame, inView: currentCell.LabelCellTitle.superview, permittedArrowDirections: UIPopoverArrowDirection.Left, animated: true)

And to dismiss

func popoverControllerDidDismissPopover(popoverController: UIPopoverController!) {

    popover.dismissPopoverAnimated(false) // just to check

    self.popover = nil
    self.popoverContent = nil

}

Any ideas?

Edit:

If I check with:

if(self.popoverContent == nil) {

before opening it, I'll find out that it's not nil when I tap the same cell again.

Edit again:

I have the same problem if I create it with a little different setup:

Custom 1x1px Button. Connect popover with segue. On cell tap move button to cell and open popover.

So there is no code for opening the popover, only with storyboard editor.

I get the same error message (sometimes) just if I tap the same popover again.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
derdida
  • 14,784
  • 16
  • 90
  • 139

9 Answers9

137

I had this issue because I was trying to perform segue / present from within:

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex

I changed it to:

- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex

and it fixed it!

Steve
  • 2,526
  • 2
  • 20
  • 30
55

I'm not on swift yet, but for Objective-C, I ended up wrapping the presentViewController call in a performSelector call.

-(void) present
{
    [self performSelector: @selector(ShowModalTableViewController) withObject: nil afterDelay: 0];
}

-(void) ShowModalTableViewController
{
    [self presentViewController: ctrlModalTableViewController animated: true completion: nil];
}
Aace
  • 1,792
  • 16
  • 15
  • Not sure why this was down-voted. This actually fixed the issue for me. – Travis M. Sep 19 '14 at 16:26
  • I don't know what is happening with iOS but I'm glad there are people like @Acace which saved my day! – Josip B. Oct 01 '14 at 13:25
  • 4
    I had an app that worked perfectly in IOS7 but had this issue in IOS8. This tip fixed it. – lihudi Oct 06 '14 at 07:56
  • 10
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ /* put code to execute here */ }); also fixed it on iOS 8 for me, in case you don't want to create a new method just to call the same code. – Zhang Oct 06 '14 at 08:19
  • 2
    If in case you are presenting from within ActionSheet button click - try @Steve's answer for using didDismissWithButtonIndex – Thiru Nov 12 '14 at 09:15
  • 2
    The above does WORK AROUND the problem/bug in iOS 8. But it's a hack way to do it. Steve's answer to switch to using didDismissWithButtonIndex: is the better solution. LOOK AT THE STEVE'S ANSWER. It's less work and much less fragile. – Daniel Dec 17 '14 at 23:14
  • Here is another solution and explanation on why this is happening. http://stackoverflow.com/questions/24942282/uiimagepickercontroller-not-presenting-in-ios-8 – aryaxt Jun 05 '15 at 22:11
  • Just for the records, if you have already defined a segue in storyboard on tap of a button or cell tap and you fire the segue programmatically as well, then this can cause the said error. – nikhil.thakkar Mar 05 '16 at 07:39
  • I have tried every possible solution, but this was the only one that worked. Thanks – AndyH Oct 13 '16 at 12:29
52

This will work also:

dispatch_async(dispatch_get_main_queue(), ^ {
        [self presentViewController:vc animated:YES completion:nil];
});
Anorak
  • 3,096
  • 1
  • 14
  • 11
Elijah
  • 8,381
  • 2
  • 55
  • 49
  • 4
    This solved a similar problem for me. However I don't understand why this works. Why do I need to do this? Any links to anything that will give more background info? – Erik Engheim Nov 12 '14 at 13:55
  • It runs presentViewController on a different thread, so there is no longer a conflict. As to why - because Apple must have changed the way they are handling these things behind the scenes. – Elijah Nov 15 '14 at 23:50
  • Different thread? I am not following. dispatch_get_main_queue() should give you the main UI thread right, which is what I am calling this from. – Erik Engheim Nov 17 '14 at 12:54
  • Yes, it is the main thread, but it's being dispatched asynchronously, so it's not preventing the other activivities from happening. Here is some discussion on GCD http://jeffreysambells.com/2013/03/01/asynchronous-operations-in-ios-with-grand-central-dispatch – Elijah Nov 17 '14 at 13:30
  • 4
    I think the solution you proposed might work but has to be consider as a nasty hack. it is not solving the source of the problem which is showing pop up before dismissing the previous one. Make sure you have dismissed the old one and then present the new one. It works as it adds a change to the next run on the main run loop – Julian Apr 01 '15 at 21:00
  • I got issue in iPad but after use your code it worked for me in IOS 9. – Mihir Oza Sep 29 '16 at 09:40
  • 1
    thanks so much, it saved my day, i found this issue happened on my iPad but iPhone are no problem at all – Deeper Sep 05 '17 at 07:52
5

It appears this is true of presenting anything over a popover. The reason all of the previous responses work is likely because whatever action is being taken happens before the popover (or activity sheet, or similar) is dismissed.

If you can, try dismissing the popover first, then presenting your modal.

Jeff
  • 5,746
  • 4
  • 33
  • 40
4

This is a iOS 8 issue on iPad actually and will not occur in iOS below 8. You can put a condition there:

if ([controller respondsToSelector:@selector(popoverPresentationController)])
{
    // iOS8
    controller.popoverPresentationController.sourceView = self.view; // or any of your UIiew
}

But consider Steve's point also (https://stackoverflow.com/a/26380194/362310) below with this code change.

Community
  • 1
  • 1
Vaibhav Saran
  • 12,848
  • 3
  • 65
  • 75
4

Easily just add this code snippet into ViewDidLoad() of your main uiviewcontroller

 definesPresentationContext = true
Fadi
  • 733
  • 1
  • 5
  • 17
3

This happens if a UIActionSheet is presented at the moment of new view controller presentation call, for example This code works for me

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [controller presentViewController:modalViewController animated:YES completion:NULL];
    }];
Tim
  • 1,877
  • 19
  • 27
1

For me, this happened when I had two UIBarButton items as part of a NavigationItem and both had a triggered segue to open views as popover - with their own controllers. One popover would not automatically dismiss when tapping the other BarButtonItem. It would however dismiss when I tapped elsewhere outside the popover. I ended up overriding UINavigationController and adding an extended version of presentViewController:animated:completion

/*
 * Workaround for apparent bug in iPad that popover does not automatically dismiss if another bar button item is pressed
 */

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {

    /*
     * Make sure this runs in the main queue
     */
    dispatch_async(dispatch_get_main_queue(), ^ {
        if (
            [self.presentedViewController isKindOfClass:[ViewController1 class]]
            || [self.presentedViewController isKindOfClass:[ViewController2 class]]
            || [self.presentedViewController isKindOfClass:[ViewController3 class]]
            || [self.presentedViewController isKindOfClass:[ViewController4 class]]
            ) {
            [self dismissViewControllerAnimated:YES completion:^{
                [super presentViewController:viewControllerToPresent animated:flag completion:completion];
            }];
        }

        else {
            [super presentViewController:viewControllerToPresent animated:flag completion:completion];
        }
    });
}

I believe the dispatch_async(dispatch_get_main_queue(), ^ {}) is not really necessary, I just added it as precaution.

zero0cool
  • 352
  • 4
  • 11
0

In my case, I accidentally link my button to an IBAction and Storyboard Segue with kind Present As Popover at the same time. What I did to fix this was to remove my button's Touch Up Inside event link to IBAction and used only Storyboard Segue.

Willy
  • 9,681
  • 5
  • 26
  • 25