酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

iOSで UIAlertControllerを表示すると「width == - 16」の制約エラーが発生する

iOS 13.5 になっても一向に修正される気配がないので仕方なく対応することにした。この現象はiOS 12.2〜13.5で発生することが確認されているらしい。

UIAlertControllerを表示すると制約エラーが発生する

ごく普通に UIAlertController を使ってアクションシートを表示します。

        let alert = UIAlertController(title: "タイトル", message: nil, preferredStyle: UIAlertController.Style.actionSheet)
//...
        present(alert, animated: true, completion: nil)

この時、下記のような制約エラーが発生しています。

[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:0x600003771fe0 UIView:0x7fe601762650.width == - 16   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600003771fe0 UIView:0x7fe601762650.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.

解決策

ios - Swift default AlertViewController breaking constraints - Stack Overflow」を参考にして、pruneNegativeWidthConstraints() メソッドを追加します。

extension UIAlertController {
    
    func pruneNegativeWidthConstraints() {
        if #available(iOS 14.0, *) {
            
        } else if #available(iOS 12.2, *) {
            // 12.2〜13.5で発生することが確認されている
            for subView in self.view.subviews {
                for constraint in subView.constraints where constraint.debugDescription.contains("width == - 16") {
                    subView.removeConstraint(constraint)
                }
            }
        } else {
            
        }
    }
}

使い方としてはこんな感じ。

        let alert = UIAlertController(title: "タイトル", message: nil, preferredStyle: UIAlertController.Style.actionSheet)
//...

        alert.pruneNegativeWidthConstraints()
        present(alert, animated: true, completion: nil)