Search code examples
iosipadnslayoutconstraintuialertcontroller

UIAlertController shown on iPhone, not on iPad


I'm presenting a UIAlertController action sheet on a view controller. This works fine on an iPhone. On an iPad I see nothing, but the console warns me about layout constraints which seem to set its height to 0:

2022-02-22 22:00:26.032174+0100 Sandbox[15099:651165] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x6000024aecb0 h=--& v=--& UIView:0x155609dd0.width == 0   (active)>",
    "<NSLayoutConstraint:0x6000024ad0e0 UIView:0x15550be40.width == 304   (active)>",
    "<NSLayoutConstraint:0x6000024926c0 _UIAlertControllerView:0x15550b850.width >= UIView:0x15550be40.width   (active)>",
    "<NSLayoutConstraint:0x6000024ae260 UIView:0x155609dd0.width == _UIAlertControllerView:0x15550b850.width   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000024ad0e0 UIView:0x15550be40.width == 304   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2022-02-22 22:00:26.032639+0100 Sandbox[15099:651165] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x6000024aed50 h=--& v=--& UIView:0x155609dd0.height == 13   (active)>",
    "<NSLayoutConstraint:0x600002492710 _UIAlertControllerView:0x15550b850.height == UIView:0x15550be40.height   (active)>",
    "<NSLayoutConstraint:0x6000024adc70 UIView:0x155609dd0.height == _UIAlertControllerView:0x15550b850.height   (active)>",
    "<NSLayoutConstraint:0x600002491f90 UIView:0x15550be40.height >= 44   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600002491f90 UIView:0x15550be40.height >= 44   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

To reproduce this, you can create a new App in Xcode (I'm using the options Storyboard, Objective-C) and change the ViewController.m code to

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognized:)]];
}

- (void)gestureRecognized:(id)sender {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:NULL]];
    alertController.popoverPresentationController.sourceView = self.view;
    [self presentViewController:alertController animated:YES completion:NULL];
}

@end

In case it matters, I'm using Xcode 13.2.1 with iOS 15.2 simulators (iPhone SE - 2nd generation and iPad 9th generation) but users with real devices are able to reproduce it as well.


Solution

  • If you look at the documentation for the sourceView property (https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller/1622313-sourceview), you see that it is defined as "The view containing the anchor rectangle for the popover."

    This means that your popover is appearing off screen, since the anchor rectangle is the view itself.

    To fix this, try modify the sourceRect property as well. Here's an example:

    alertController.popoverPresentationController.sourceRect = CGRectMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds), 1, 1);
    

    You will see that this causes the alert to appear in the middle of the view.

    popover