13

I'm trying to figure out how to display a UIAlertController with a style of UIAlertControllerStyleActionSheet within my iMessage app extension.

The problem is, the action sheet appears below the native iMessage text field when presented when calling:

[self.view.window.rootViewController presentViewController:actionSheetController animated:YES completion:NULL];

How would I go about fixing this?

Code:

UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Clear", nil) message:nil preferredStyle:UIAlertControllerStyleActionSheet];

UIAlertAction *clear = [UIAlertAction actionWithTitle:NSLocalizedString(@"Clear", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action)
{
    [self clear];
}];

UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action)
{}];

[actionSheetController addAction:clear];
[actionSheetController addAction:cancel];

[self.view.window.rootViewController presentViewController:actionSheetController animated:YES completion:NULL];

enter image description here

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
klcjr89
  • 5,862
  • 10
  • 58
  • 91
  • Can you present `UIAlertController` on `MSMessagesAppViewController`? – Bharat Nakum Oct 07 '16 at 07:30
  • This is a known issue. From the iOS 10/10.1 release notes: "When a UIAlertController object is presented in a Messages extension, it is truncated by the bottom bar of the extension." Open a bug report to tell Apple this is important to you. – Léo Natan Oct 07 '16 at 17:58
  • iMessage text field belongs to some window which is higher than your `self.view.window` but lower than the status bar. Probably you may want to try to add a new `UIWindow` with `windowLevel = UIWindowLevelStatusBar` and present the alert from that window – Vlad Fedoseev Oct 07 '16 at 19:05
  • @VladFedoseev tried this just now and it didn't work. `UIWindow *window = UIWindow.new; window.rootViewController = self; window.windowLevel = UIWindowLevelStatusBar; [window makeKeyAndVisible]; [window.rootViewController presentViewController:alertController animated:YES completion:NULL];` – klcjr89 Oct 08 '16 at 01:08
  • I have no problem presenting a UIAlertController with AlertStyle. However, I get occasional crashes on dismissal, so I would suggest create a UIView and add as subview to MSMessagesAppViewController – user1055568 Jan 01 '17 at 15:04

4 Answers4

3

Here is a workaround. Maybe add a little animation to make it smooth.

[self.view.window.rootViewController presentViewController:actionSheetController animated:YES completion:^{
    actionSheetController.view.frame = CGRectOffset(actionSheetController.view.frame, 0, -40);
}];
Sudo
  • 991
  • 9
  • 24
3

Another workaround:

actionSheetController.view.transform = CGAffineTransform(translationX: 0, y: -40)    
[self.view.window.rootViewController presentViewController:actionSheetController animated:YES completion:NULL];
Max Gribov
  • 388
  • 2
  • 7
  • 1
    Also you can use `UIAlertControllerStyleAlert` for your `UIAlertController preferredStyle:`... Alert always appear in the middle of screen (even on ipad)... Finally I used it by myself... – Max Gribov Oct 13 '16 at 18:52
0

Do not hardcode the y-position for the action sheet.

This could lead to ui issues on later iOS major updates, when apple changes the height of the iMessage dock. Instead use the safeAreaInsets.bottom value in order to find out by which value the view is hidden by the dock.

Here is the solution we are using:

    let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

    actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (_) in
        actionSheet.dismiss(animated: true, completion: nil)
    }))

    // Adding further actions...

    present(actionSheet, animated: true) {
        UIView.animate(withDuration: 0.2, animations: {
            actionSheet.view.frame = CGRect(x: actionSheet.view.frame.minX,
                                            y: actionSheet.view.frame.minY - self.view.safeAreaInsets.bottom,
                                            width: actionSheet.view.frame.width,
                                            height: actionSheet.view.frame.height)
        })
    }

Detail: The action sheet is hidden by the iMessage dock, because the view controllers view over which you are presenting the action sheet is tied up to the bottom of the view and not the top of the iMessage dock. This case must be considered e.g. when adding constraints to other views and its subview.


Update:

Just use the following code in order to avoid the animation above. In my case after time seeing the animation did get pretty annoying.

extension UIAlertController {
    open override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        guard let viewController = presentingViewController else { return }
        view.transform = .identity
        view.transform = .init(translationX: 0.0, y: - viewController.view.safeAreaInsets.bottom)
    }
}
Baran
  • 2,710
  • 1
  • 23
  • 23
-1

according to this, you can request full screen presentation by:

[self requestPresentationStyle:MSMessagesAppPresentationStyleExpanded];

before your code:

[self.view.window.rootViewController presentViewController:actionSheetController animated:YES completion:NULL];
Allen
  • 6,505
  • 16
  • 19