Search code examples
iosautolayoutuikituialertcontroller

Presenting UIAlertController (in actionsheet style) caused mysterious autolayout warning


The issue can be reproduced with a simple project as below:

enter image description here

When user clicks the + button on the navigation bar, the code instantiates and presents an UIAlertController in actionsheet style.

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func buttonTapped(_ sender: Any) {
        let actionSheet = UIAlertController(title: nil,
                                            message: nil,
                                            preferredStyle: .actionSheet)
        actionSheet.addAction(UIAlertAction(title: "Create Income",
                                            style: .default) { _ in
                                                print("Income!") })
        actionSheet.addAction(UIAlertAction(title: "Create Outcome",
                                            style: .default) { _ in
                                                print("Outcome!")})
        actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in })
        self.present(actionSheet, animated: true, completion: nil)
    }
}

The UI shows up correctly. The issue is Xcode emits the following autolayout warnings when the action sheet appears:

2020-03-12 11:24:57.002113+0800 alertcontroler_test[12757:936231] [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. 
(
    "<NSLayoutConstraint:0x600002d11b30 UIView:0x7f851961c560.width == - 16   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600002d11b30 UIView:0x7f851961c560.width == - 16   (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.

I check UI hierarchy in Xcode when the action sheet is showing up and find some subviews are followed by a exclamation mark:

enter image description here

I google about this but don't find people reporting similar issue (that is, autolayout warnings when UIAlertController shows up). I find some information about how to set break point on UIViewAlertForUnsatisfiableConstraints() and how to debug assemble code. But I think this is an internal issue in UIAlertController and the way how I create and present UIAlertController is common and typical. Any suggestion what I should do?

This is found on latest version of Xcode (11.3.1) and iOS (13.3). The issue is gone if I change style to alert. Any help would be greatly appreciated.


Solution

  • This is a bug which is not fixed by Apple team this bug is related to alerts and action sheets related to animation. You may follow these links to verify this:-

    UIAlertController's actionSheet gives constraint error on iOS 12.2 / 12.3

    Swift default AlertViewController breaking constraints

    The solution for this is to pass animation value as false like:-

    controller.present(alertController, animated: false, completion: nil)
    

    I have present the UIAlertController without animation and warning got vanished.

    or you may try this solution mentioned in the one of the links of stackoverflow:-

    @IBAction func buttonTapped(_ sender: UIButton) {
     ...
    self.present(alertController,animated: true,completion: nil)
    
    
    
    alertController.view.subviews.flatMap({$0.constraints}).filter{ (one: NSLayoutConstraint)-> (Bool)  in
      return (one.constant < 0) && (one.secondItem == nil) &&  (one.firstAttribute == .width)
    
    
    }.first?.isActive = false
    
      }