52
votes

I have a storyboard with Autolayout and Size Classes. Pretty complicated layout, and unfortunately I can't really pin down how to reproduce the problem in a new project.
But the view in question is pinned to the left and right edge of the screen with a constraint that has 750 priority (i.e. |-(0@750)-[myView]-(0@750)-|, additionally it has a greater or equal than constraint with a priority of 1000 (i.e. |-(>=0)-[myView]-(>=0)-|). That is done to limit the width on an iPad, so there is a width constraint of width <= 600 @1000, and a center horizontal in container constraint as well. On top of that the view has a aspect ratio constraint of 3:1. As I said, rather complicated.

Interface Builder doesn't show any problems with the constraints. The Xcode layout preview renders correct for all devices.

When I run the app iOS tells me that it has conflicting constraints.

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) 
(
    "<NSLayoutConstraint:0x7fd72853ff80 DMXKit.DipSwitchAssembly:0x7fd72990d0e0.width == 3*DMXKit.DipSwitchAssembly:0x7fd72990d0e0.height>",
    "<NSLayoutConstraint:0x7fd728574e50 '_UITemporaryLayoutWidth' H:[DMXKit.DipSwitchAssembly:0x7fd72990d0e0(400)]>",
    "<NSLayoutConstraint:0x7fd72856e9c0 V:[DMXKit.DipSwitchAssembly:0x7fd72990d0e0(133)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7ffb19798000 DMXKit.DipSwitchAssembly:0x7ffb1979a000.width == 3*DMXKit.DipSwitchAssembly:0x7ffb1979a000.height>

This is repeated a couple of times, with the exact same (i.e. same pointer) constraints. That is strange as well, looks like the constraint isn't really broken. When run, the app looks 100% correct. When I look at the app in the Xcode view debugger, the constraint with the address 0x7ffb19798000 is still there, so it was never broken.

Where does the _UITemporaryLayoutWidth constaint comes from? Obviously I didn't add it. Google doesn't spit out anything useful about _UITemporaryLayoutWidth. Did anyone encounter a problem like this?

12
I have a lot of these warnings in the log as well. Although, it doesn't seem to break my layout. I wonder where these _UITemporaryLayoutWidths come from.dergab

12 Answers

78
votes

So, I'm not sure if this helps you with your problem since it sounds like you're building your layout in IB, but here's an issue that I just ran into that may help others that google search for "_UITemporaryLayoutWidth".

My scenario was that I was adding auto layout constraints during init and was accidentally triggering a 'layoutIfNeeded' before adding the view to the view hierarchy (modifying a buttons edge insets triggers layout). It looks like the system adds these temporary constraints and (I assume?) removes them after the view is added to the hierarchy. The temp constraints set 0 dimensions (in my case) which can fail if you, for example, set specific constraint constant values in your layout (e.g., you want 16px between these buttons but there's 0 space in that dimension...)

Update: Investigated a little more and found that the temporary constraint seems to correspond to the frame that you use to initialize the parent view. In my case, the frame I used was size 0x0, so the temp constraint evaluated relative to 0x0 size, thus failing. Verified that if I init with a 5x5 frame then I see the same error with _UITemporaryLayoutWidth(5) constraint. The other thing that I've noticed is that the constraint seems to be added and removed during the layout evaluation - if I break right before I trigger the layout and analyze the constraints then I don't see the temp constraints. I also don't see the constraints after the error, so I suspect they synthesize the temp constraints, add them, solve then remove them.

Update 2: Ok, maybe this is TMI but this might be of some use to someone. My current thought is that these temp constraints are a peek into how the system handles mixed frame manip and autolayout within a single view hierarchy. In my case, my view had no parent and had no constraints defining its size, but it did have a zero frame which meant the system assumed that's the size it should use when solving the layout. My thought is that the system synthesizes these temp constraints for views that have their frames set explicitly to solve the rest of the system as it's traversing the hierarchy.

Update 3: So, I ran into this problem again and wanted to share a little more info. My scenario was that I was trying to essentially measure views that had no parent using the systemLayoutSizeFittingSize: method. Long story short, I had to call layoutIfNeeded on the view before measuring it - this is where I run into the conflict with the temp constraints since the view doesn't have a superview. The temp constraint constants (the dimensions) do correspond to whatever frame is set on the view without a superview - I think this makes sense (though, setting a frame on a view that you're going to be using with AutoLayout seems odd...) I was able to get around the temp constraint issue by saying - if the view doesn't have a superview then add it to a temporary superview; measure; remove it from the temporary superview. Hope this is useful.

20
votes

As mentioned above the temporary constraints appear to be set on views that don't have a parent view. I found that ensuring translatesAutoresizingMaskIntoConstraints is false on the top level view removed the temporary constraints. As:

topView.translatesAutoresizingMaskIntoConstraints = false
13
votes

I my case, the problem was the following:

I created a UIButton.
I set its width and height constraints.
I added the button to its superview.
I set its leading and top layout constraints to the superview.

This created the error "Unable to simultaneously satisfy constraints" and one of the conflicting constraints was _UITemporaryLayoutHeight.

When I added the button to its superview before setting its width and height constraints, the error was gone.

8
votes

In my situation , I got this UITemporaryLayoutWidth conflict in this situation only

  1. i create a view with [UIView new] , and i did not add it to any superview
  2. then i create a width constraint to this view just be created
  3. invoke layoutIfNeeded to this view immediately,and the conflict happens

so the way to eliminate this error is simple : don't call layoutIfNeeded to it self before added to some superview

4
votes

I faced this issue when I added my constraints with layout-margins anchors of a view. When I changed the constraints from layout-margins anchors to top, bottom, leading, trailing anchors of the view, the error vanished.

I was creating my superview with .zero rect.

But since I needed to incorporate margins of the view I had to do a little hack. I changed the priority of my constraints.

I was getting temporary width and temporary height errors. So I lowered the priority of one constraint along horizontal axis and one constraint along vertical axis (top and trailing constraints in this case):

    let topConstraint = stackView.topAnchor.constraint(equalTo: margins.topAnchor)
    topConstraint.priority = .init(rawValue: 999.0)
    topConstraint.isActive = true

    let trailingConstraint = stackView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
    trailingConstraint.priority = .init(rawValue: 999.0)
    trailingConstraint.isActive = true

The reason for this is that since I initialize my view with .zero rect iOS automatically adds the temporary width and height constraints. At this time if we add other constraints with high priority, it will break generating the error logs.

Lowering the priority fixes the breaking constraints and when the temporary constraints are removed, our view will get its right shape.

2
votes

Just a quick addition to the comments above, the simple solution that worked for me was to change the order in which my constraints were being created. I was programmatically creating constraints on both views and superviews - but I was doing the views before the superviews. So I swapped it around so that the superview constraints went first, then the subviews had their constraints added, and this meant that there was no longer call for the _UITemporaryLayoutWidth, and so the constraint issues went away.

2
votes

Spent a couple of hours on this problem today. I was setting the contentMode of my UIButton imageView immediately after initialising the element, which seemed to force a layout before I had set up the constraints in my top level class (Which included constraining the height/width).

2
votes

A different approach worked for me.

I am creating all my views programmatically and am setting the initial frame of the top-level view (as in the one that goes to the view property of UIViewController) and its subviews to CGRect.zero. The generated warnings looked something like this:

"<NSLayoutConstraint:0x280ebf200 UIStackView:0x136151380.width == UILayoutGuide:0x28140e3e0'UIScrollView-frameLayoutGuide'.width - 16   (active)>",
"<NSLayoutConstraint:0x280ddee40 '_UITemporaryLayoutWidth' MyApp.SearchFilterView:0x1316a8000.width == 0   (active)>",
"<NSLayoutConstraint:0x280d4b480 'UIScrollView-frameLayoutGuide-width' UILayoutGuide:0x28140e3e0'UIScrollView-frameLayoutGuide'.width == MyApp.SearchFilterView:0x1316a8000.width   (active)>"

You will notice from the second line that _UITemporaryLayoutWidth is initially being set to zero.

I've wrestled with giving some of the subviews minimum width constraints, but only giving the top-level view a non-zero frame removed all Auto Layout warnings involving _UITemporaryLayoutWidth. I specifically used UIScreen.main.bounds.

class SearchFilterView {
  
  init( ... ) {
    // Instantiate properties here...
    super.init(frame: UIScreen.main.bounds)
  }

}
1
votes

My UITemporaryLayoutWidth Issue was I was creating buttons programmatically and the tempWidth constraint would be around 0.1 off my width constraint. (Though annoyingly this only happened when creating buttons in one of the multiple ways I created buttons)

The issue was I was using .adjustsFontSizeToFitWidth = true for the button title.

I commented out that line and it went away.

0
votes

The root of the problem is the 3:1 aspect ratio. If I change it to something else it works, I now use 4:1.4, which works. I don't know why, but it works.

I don't know a lot about the internals of auto layout, but I think, the problem is the order how the layout engine wants to fulfill the constraints. Size classes might be the reason why the temporary width constraint is added.

The unsatisfiable constraints don't appear to be a real problem because it looks like they are unsatisfiable only temporarily. It's just a couple lines of log that appear each time the view is created.

0
votes

I think the issue is adding constraint to a view before adding it as subview to another view. If you assign constraints when there is no superview it automatically adds _TemporaryLayout constraints.To prevent that make translatesAutoResizingMaskIntoConstraints false.

In my case I was adding a view controller as another view controllers child. Even I set translatesAutoResizingMaskIntroConstraints flag to false from where I add it to parent view controller the warnings were still there.

Then I found loadView method of childViewController called when the view of childViewController is first called. Therefore it does not work when you set autoresizing flag outside of the childViewController since it would already set their constraints.

ParentViewController

let childViewController = CustomViewController()
//childViewController.view.translatesAutoResizingMaskIntoConstraints = false 
// This does not work since before this line executes loadView 

parentViewController.addChild(childViewController)
parentViewController.view.addSubview(childViewContoller.view) 
// `childViewContoller.view` triggers loadView method of childViewController
// It sets up constraints before adding as subview.

solution is making translatesAutoResizingMaskIntoConstraints false in childViewController

override func loadView() {

     super.loadView() // If you used storyboard call this to set those views
     // view = UIView() // call this if you layout views programatically 
    view.translatesAutoResizingMaskIntoConstraints = false
}
0
votes

I got this when I created a view and inadvertently animated one of its properties before it was presented.