Properly Present a UIAlertController from MSMessagesAppViewController
Asked Answered
D

4

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

Daysidayspring answered 29/9, 2016 at 1:15 Comment(5)
Can you present UIAlertController on MSMessagesAppViewController?Whole
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.Puberty
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 windowNeighboring
@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];Daysidayspring
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 MSMessagesAppViewControllerBerzelius
M
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);
}];
Marnamarne answered 7/10, 2016 at 15:15 Comment(2)
This is problematic, as any layout pass will move the alert controller's view to its original position.Puberty
@LeoNatan you could you KVO center property and re-adjust frame if neededHoneydew
M
3

Another workaround:

actionSheetController.view.transform = CGAffineTransform(translationX: 0, y: -40)    
[self.view.window.rootViewController presentViewController:actionSheetController animated:YES completion:NULL];
Magnetics answered 13/10, 2016 at 18:45 Comment(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...Magnetics
M
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)
    }
}
Middleaged answered 6/7, 2018 at 17:2 Comment(0)
D
-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];
Disraeli answered 7/10, 2016 at 17:48 Comment(1)
Doesn't help. Even when expanded, the alert controller still appears under the input bar.Puberty

© 2022 - 2024 — McMap. All rights reserved.