Fix: Unable to simultaneously satisfy constraints in UIKit

Fix: Unable to simultaneously satisfy constraints in UIKit

Written by

in

Quick answer: The conflict comes from the view’s autoresizing mask: a view created in code keeps translatesAutoresizingMaskIntoConstraints = true, so UIKit converts its frame into required constraints that fight the ones you add. Set it to false before adding anything, and replace your width and height constraints — width == width with constant 0 defines nothing — with constant constraints or anchor constraints.

Why this happens

UIView(frame:) gives the view a frame, and a view created in code defaults to translatesAutoresizingMaskIntoConstraints = true. That flag tells UIKit to convert the frame into a set of required layout constraints — the NSAutoresizingMaskLayoutConstraint lines in your log, the ones with h=--& v=--& and values like midX == + 50.

Those generated constraints are required. When you then add centerX == view.centerX on top of them, Auto Layout has two independent required position/size sources and drops one of your constraints with Unable to simultaneously satisfy constraints. The log even names the victim in the Will attempt to recover by breaking constraint line.

The second problem is separate but related: your width and height constraints are written as newView.width == newView.width with constant 0. That is a tautology — it holds for any width — so it never pins the size to 100. Once the autoresizing mask is gone, the view would have no intrinsic size and would collapse or stretch.

How to fix it

1. Turn off the autoresizing mask translation

This is the fix for the log itself. Set the flag before adding constraints, so the frame is no longer exported as constraints:

let newView = UIView()
newView.backgroundColor = .systemRed
newView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(newView)

Notice that the frame is gone too. Once the flag is false, the frame is an output of Auto Layout, not an input, so UIView(frame:) buys you nothing. Create the view with UIView() and let the constraints decide its geometry.

2. Give the view an actual size

A constant size constraint must not point at the view itself. In the older initializer form, pass nil and .notAnAttribute:

let width = NSLayoutConstraint(item: newView, attribute: .width,
                               relatedBy: .equal,
                               toItem: nil, attribute: .notAnAttribute,
                               multiplier: 1, constant: 100)
let height = NSLayoutConstraint(item: newView, attribute: .height,
                                relatedBy: .equal,
                                toItem: nil, attribute: .notAnAttribute,
                                multiplier: 1, constant: 100)

With anchors it is simply equalToConstant:

newView.widthAnchor.constraint(equalToConstant: 100)
newView.heightAnchor.constraint(equalToConstant: 100)

3. Switch to the anchor API and activate the constraints

The full working version of the original snippet, centered and 100×100:

override func viewDidLoad() {
    super.viewDidLoad()

    let newView = UIView()
    newView.backgroundColor = .systemRed
    newView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(newView)

    NSLayoutConstraint.activate([
        newView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        newView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        newView.widthAnchor.constraint(equalToConstant: 100),
        newView.heightAnchor.constraint(equalToConstant: 100)
    ])
}

The equivalent without anchors, if you are maintaining older code:

newView.translatesAutoresizingMaskIntoConstraints = false

let centerX = NSLayoutConstraint(item: newView, attribute: .centerX, relatedBy: .equal,
                                  toItem: view, attribute: .centerX,
                                  multiplier: 1, constant: 0)
let centerY = NSLayoutConstraint(item: newView, attribute: .centerY, relatedBy: .equal,
                                  toItem: view, attribute: .centerY,
                                  multiplier: 1, constant: 0)
let width = NSLayoutConstraint(item: newView, attribute: .width, relatedBy: .equal,
                               toItem: nil, attribute: .notAnAttribute,
                               multiplier: 1, constant: 100)
let height = NSLayoutConstraint(item: newView, attribute: .height, relatedBy: .equal,
                                toItem: nil, attribute: .notAnAttribute,
                                multiplier: 1, constant: 100)

NSLayoutConstraint.activate([centerX, centerY, width, height])

NSLayoutConstraint.activate(_:) adds each constraint to the correct view for you. If you prefer the older style, constraint.isActive = true does the same thing one at a time.

4. Attach constraints to the right view

view.addConstraint(...) requires the receiving view to be the nearest common ancestor of the items in the constraint. A width constraint on newView only involves newView, so it belongs on newView, not on self.view. Adding it to the controller’s view can produce confusing behavior or the Unable to activate constraint with anchors ... because they have no common ancestor exception when using anchors. Use activate and avoid the question entirely.

5. Debug what is left with a symbolic breakpoint

The log already tells you what to do: add a symbolic breakpoint on UIViewAlertForUnsatisfiableConstraints (Breakpoint Navigator → + → Symbolic Breakpoint). It pauses at the exact moment the conflict is detected, so you can inspect which view and which constraints are involved instead of guessing from the printed list.

If the conflict is intentional — a layout that should give way at some size — do not remove the constraint; lower its priority:

let width = newView.widthAnchor.constraint(equalToConstant: 100)
width.priority = .defaultHigh
width.isActive = true

How to prevent it

  • Set translatesAutoresizingMaskIntoConstraints = false immediately after creating any view you lay out in code.
  • Do not mix frame and constraints for the same view; pick one and stay with it.
  • Write size constraints as equalToConstant or with toItem: nil, attribute: .notAnAttribute, never as a self-reference with constant 0.
  • Prefer NSLayoutConstraint.activate([...]) over individual addConstraint calls so constraints land on the right view.
  • Keep the UIViewAlertForUnsatisfiableConstraints symbolic breakpoint in your project while developing layouts.

FAQ

Why does the log mention NSAutoresizingMaskLayoutConstraints I never created?

Because the view’s translatesAutoresizingMaskIntoConstraints is still true, so UIKit generated them from its frame. They disappear as soon as you set the flag to false.

Does setting frame still do anything after I use Auto Layout?

No. With the translation flag off, frame is recalculated at layout time from the constraints, so any value you assign earlier is overwritten.

Why is width == width with constant 0 not a valid width constraint?

It compares the view’s width to itself, which is always true and constrains nothing. A constant size needs no second item, which is why the initializer takes nil and .notAnAttribute.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.