Presenting a view controller modally from an action sheet's delegate in iOS8 - iOS11
Asked Answered
O

9

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...>
Orelu answered 20/7, 2014 at 20:52 Comment(0)
O
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];
});
Orelu answered 20/7, 2014 at 20:52 Comment(9)
Even so it doesn't work for me. Popover fails. iOS8 = trashed everything.Kileykilgore
@Michael What do you mean popover fails?Salmonberry
It is hard to imagine this bug is still out there. @leo-natan do you have an update from Apple.Langlauf
@Langlauf No, the issue is still open with Apple, and reproduces on 8.1 b1.Salmonberry
@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.Kileykilgore
This workaround works for UIActionSheet's showFromRect: on iPad, iOS 8.1b2.Laurentian
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];Helotism
@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]; });Langlauf
@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.Monto
Z
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.

Zellner answered 30/9, 2014 at 2:6 Comment(10)
My solution does not block the main thread. Presenting in didDismiss causes the same issue.Salmonberry
@LeoNatan "Presenting in didDismiss causes the same issue", u mean still cannot fix the issue? Hm.. but I got it fixed perfect, view controller is presented as soon as the action sheet was dismissed :)Zellner
@LeoNatan hm.. the "block" means ur "delaying the presentation until the next runloop" ;)Zellner
In our app, the same "Attempt to present" warning appears. Perhaps it has been fixed in the new iOS 8.1 beta.Salmonberry
Delaying is the opposite of blocking.Salmonberry
@HernanArber yeah, I've lots WHYs for iOS 8 SDK.Zellner
This seems to be the best solution and I feel the outcome is more natural too.Cavatina
Very simple solution that absolutely works. Thanks!Knack
UIActionSheet is dprecated in iOS 8, so this is obviously no longer a valid answer. Thanks Apple! ... for nothing.Hate
@LeiKan glad to hear it :)Zellner
T
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

Thomasson answered 24/9, 2014 at 9:39 Comment(0)
A
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];
Arginine answered 9/2, 2015 at 9:37 Comment(1)
please do not post same issue as a answer. you can ask another question.Juniorjuniority
F
2

I fixed it in Swift 3 with the following code

  DispatchQueue.main.async {
            self.present(alertController, animated: true, completion: nil)
        }
Fog answered 21/4, 2017 at 14:37 Comment(0)
F
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.

Five answered 15/10, 2014 at 3:33 Comment(1)
Hi, welcome to stackoverflow! Please format your answer into a more readable format, and more explanation if possible.Drama
T
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 :))

Thereabout answered 4/2, 2015 at 13:27 Comment(2)
This is the same answer as @Kjuly. The system displayed the same alert on didDismissWithButtonIndex: when I asked/answered the question.Salmonberry
Sorry for that. I didn't see that answer that time :)Thereabout
C
0

Try

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    // action sheet presentation
    // or modal view controller presentation
    // or alert view presentation
}];
Cowfish answered 11/2, 2015 at 13:22 Comment(0)
P
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.

Prewitt answered 20/2, 2015 at 14:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.