58

So I noticed that in iOS8 beta 3 (Update: still happens in iOS 11.2) on iPad, when attempting to present a view controller from within a delegate method of a UIActionSheet, "nothing" happens and a log message is output to the debug console, stating that presentation was attempted while transitioning an alert controller:

Warning: Attempt to present <UIViewController: 0x...> on <ViewController: 0x...> which is already presenting <UIAlertController: 0x...>
Leo Natan
  • 55,734
  • 8
  • 140
  • 186

9 Answers9

138

Update: As of iOS 9 SDK, UIActionSheet is deprecated, so do not expect a fix regarding this issue. It is best to start using UIAlertController when possible.


The problem seems to come from Apple's switch to using UIAlertController internally to implement the functionality of alert views and action sheets. The issue is seen mostly on iPad and action sheets, because on iPad, action sheets are presented as a popover within a specified view, and what Apple does is travel the responder chain until it finds a view controller and calls presentViewController:animated:completion: with the internal UIAlertController. The problem is less obvious on iPhone and with alert views, because there Apple actually creates a separate window, an empty view controller and presents the internal UIAlertController on top of that, so it seems to not interfere with other presentation.

I have opened bug report for this issue: rdar://17742017. Please duplicate it and let Apple know this is a problem.

As a workaround, I recommend delaying the presentation until the next runloop, using the following method:

dispatch_async(dispatch_get_main_queue(), ^ {
    [self presentViewController:vc animated:YES completion:nil];
});
Leo Natan
  • 55,734
  • 8
  • 140
  • 186
  • Even so it doesn't work for me. Popover fails. iOS8 = trashed everything. – Michael Chourdakis Oct 05 '14 at 21:19
  • @Michael What do you mean popover fails? – Leo Natan Oct 05 '14 at 21:20
  • It is hard to imagine this bug is still out there. @leo-natan do you have an update from Apple. – Pichirichi Oct 07 '14 at 15:33
  • @Pichirichi No, the issue is still open with Apple, and reproduces on 8.1 b1. – Leo Natan Oct 07 '14 at 17:35
  • @LeoNatan even if I use dispatch_async, it's the same issue when an existing UIPopoverController tries to open another UIPopoverController. It fails with the same warning message. I have to destroy the existing controller first, and then open the new one. – Michael Chourdakis Oct 09 '14 at 04:30
  • 1
    This workaround works for `UIActionSheet`'s `showFromRect:` on iPad, iOS 8.1b2. – Bigood Oct 10 '14 at 09:00
  • Thanks for this solution. I actually used this to delay presentation of the popover, but same idea: [self performSelector: @selector(myCallToPresentPopover) withObject:nil afterDelay:0]; – Mark24x7 Oct 10 '14 at 16:17
  • @Michael try to replace it with: `dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.51 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self presentViewController:vc animated:YES completion:nil]; });` – Pichirichi Oct 14 '14 at 17:37
  • Thank you Leo you saved me! – Marie Dm Apr 14 '15 at 21:32
  • @Michael I guess this comment is way too late but you could try to do dispatch_after, in my experience dispatch_async not always dispatches outside of the runloop. – iur Aug 22 '17 at 10:34
31

You can try to do your job (presenting view controller) in

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

instead of

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

as @LeoNatan said, "The problem seems to come from Apple's switch to using UIAlertController internally to implement the functionality of alert views and action sheets". So you must to wait the action sheet dismissed, then present the view controller you want.

@LeoNatan's solution just block the UI at main thread, so it'll also make sure the view controller will be presented after the action sheet was dismissed.

Kjuly
  • 32,573
  • 22
  • 98
  • 112
4

unfortunately this code doesn't work for me, I think because my problem was not calling presentController method directly but in the prepareForSegue method so using

[segue destinationViewController]

I've noticed that if the segue is "push" kind all works correctly, but if it is "modal", just in ipad, i got that error.

Then I've found some new option in storyboard in the segue panel, and i sovled my problem choosing "Current context" for Presentation option

I hope this will be helpful for someone else... here is the screenshot about the option

enter image description here

Achille
  • 219
  • 2
  • 12
2

I had this same issue. I created a separate window for alerts and actionsheets in my appdelegate and presented the alerts on it. It worked for me!

   self.alertWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  // Override point for customization after application launch.
  self.alertWindow.backgroundColor = [UIColor clearColor];
  UIViewController *dummy = [[UIViewController alloc] init];
  [self.alertWindow setRootViewController:dummy];

You can present as :

[[myAppDelegate appDelegate].alertWindow makeKeyAndVisible];
  [[myAppDelegate appDelegate].alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
Kaey
  • 4,563
  • 1
  • 12
  • 18
2

I fixed it in Swift 3 with the following code

  DispatchQueue.main.async {
            self.present(alertController, animated: true, completion: nil)
        }
Joan Cardona
  • 3,011
  • 2
  • 23
  • 32
1

Issuing a

[self.navigationController dismissViewControllerAnimated:YES completion:nil];

on

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

before trying to present another modal view worked for me.

user3473830
  • 6,770
  • 5
  • 29
  • 47
Salvador
  • 11
  • 2
1

use

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

instead of

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

Action view is presented above current VC so thats what causes warning/error. when didDismiss is called, action view is already dismissed, so no problems at all :))

zurakach
  • 1,074
  • 7
  • 14
0

Try

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    // action sheet presentation
    // or modal view controller presentation
    // or alert view presentation
}];
Stuart
  • 34,797
  • 19
  • 93
  • 135
Tim
  • 1,707
  • 17
  • 24
0

In iOS 8 Apple uses UIAlertController internally to implement the functionality of alert views and action sheets. So when you want to show a UIViewController modally after displaying UIActionSheet or UIAlertView in delegate method like

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

and

(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

you have to first dismiss UIAlertController as follows:

if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0"))
{
    UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
    [vc dismissViewControllerAnimated:NO completion:^{

    }];
}

Now you can present a modal UIViewController in iOS 8.

Pankaj purohit
  • 475
  • 2
  • 9