2

Try to use the code from the first answer in the link (by Kampai): How to use UIAlertController to replace UIActionSheet?

However, the completion handler is not even called in my code.

The alert action sheet can be dismissed after pressing both buttons but nothing inside the completion handler works. Any idea what might be the problem? I'm new to using completion handler and have tried to find answers online, but few have the same problem as mine.

- (IBAction)takePhotoButtonPressed:(UIButton *)sender {
pressedButtonTagNumber = sender.tag;

UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];

[actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {

    // Cancel button tappped
    [self dismissViewControllerAnimated:YES completion:^{
    }];
}]];

[actionSheet addAction:[UIAlertAction actionWithTitle:@"Take a Photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    NSLog(@"!");
    // Take a Photo button tapped
    [self dismissViewControllerAnimated:YES completion:^{
        NSLog(@"0"); // NOT CALLED
        // Initialize UIImagePickerController
        UIImagePickerController *takePhotoImagePickerController = [[UIImagePickerController alloc] init];            takePhotoImagePickerController.delegate = self;
        takePhotoImagePickerController.allowsEditing = YES;
        NSLog(@"1");
        // Check and assign image source
        if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
            NSLog(@"2");
            UIAlertController *noCameraErrorSheet = [UIAlertController alertControllerWithTitle:@"Camera is not available" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
            [noCameraErrorSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
                // Cancel button tappped
                [self dismissViewControllerAnimated:YES completion:^{
                }];
            }]];
        } else {
            takePhotoImagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
            // Present UIImagePickerController
            [self presentViewController:takePhotoImagePickerController animated:YES completion:NULL];
        }

    }];
}]];

Solution:

@Paulw11 solution works great:

1) No need to dismissViewController for UIAlertController. 2) Cannot call a new UIAlertController if the one wraps it is dismissing (obviously). 3) Better to check and disable the button in advance.

UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];

[actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
}]];

UIAlertAction *takePhotoActionButton = [UIAlertAction actionWithTitle:@"Take Photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    [self takePhoto];
}];
UIAlertAction *uploadPhotoActionButton = [UIAlertAction actionWithTitle:@"Upload from Library" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    [self uploadPhoto];
}];

// Disable take a photo button if source not available
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
    [takePhotoActionButton setEnabled:FALSE];
}
// Disable upload a photo button if source not available
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
    [uploadPhotoActionButton setEnabled:FALSE];
}

[actionSheet addAction:takePhotoActionButton];
[actionSheet addAction:uploadPhotoActionButton];

// Present action sheet.
[self presentViewController:actionSheet animated:YES completion:nil];
Community
  • 1
  • 1
Chenya Zhang
  • 433
  • 2
  • 7
  • 21
  • 2
    Are you calling `dismissViewController:animated:` to dismiss the UIAlertController? If so, don't do that. It is automatically dismissed when a button is tapped. In the action handler you just need to write whatever code you want to execute when the action button is selected. E.g. Your "cancel" action would be empty – Paulw11 Aug 12 '16 at 02:30
  • Thanks! I also just found that. Just wondering, when should I use "[self dismissViewControllerAnimated:YES completion:^{ }];"? Why this completion handler not work? – Chenya Zhang Aug 12 '16 at 02:40
  • You use that whenever you want to dismiss a view controller. The reason you don't use it here is that `UIAlertController` has already dismissed itself. Say you wanted a timeout, where the alert was cancelled after 5 seconds if the user didn't choose an option; you could start a timer and after 5 seconds call `dismissViewController:animated:` to remove the alert. – Paulw11 Aug 12 '16 at 02:43
  • @Paulw11 Very clear explanation. – Chenya Zhang Aug 12 '16 at 02:52

1 Answers1

5

You don't need to call dismissViewController:animated: from within the action handler to remove the alert. UIAlertController calls this to dismiss itself prior to invoking the action handler code.

In your action handler you simply need to execute whatever should be done when that action is selected:

In this case:

  • In your cancel action you don't need to do anything
  • In your "take a photo" action you take a photo

Also, from a user experience point of view, it may be better to disable the "take a photo" or present an alert as soon as they select it rather than issuing an alert after they have attempted to take a photo; in other words indicate the problem earlier rather than later

Paulw11
  • 95,291
  • 12
  • 135
  • 153
  • Another question on alerting the user. Now I always got the exception that "Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior ()". Is it because I put another UIAlertController inside the current one? I'm a little confused "who" is deallocating as referred in the error message. Have searched online and try to remove the UIAlertController from the parent controller and etc., nothing works now. – Chenya Zhang Aug 12 '16 at 03:01
  • Yes, because you are going to try and present that second alert controller on the first, but the first is being dismissed. As I said, you should check whether the camera is available before displaying the first alert. – Paulw11 Aug 12 '16 at 03:03