Fuck you UITemporaryLayoutWidth constraint

Today I spent 30 minutes to fix this Auto Layout contstraint warning:

(
    "<NSLayoutConstraint:0x600001890f00 UIStackView:0x7ff1d716f520.leading == UILayoutGuide:0x6000002d4a80'UIViewLayoutMarginsGuide'.leading   (active)>",
    "<NSLayoutConstraint:0x600001890f50 UIStackView:0x7ff1d716f520.trailing == UILayoutGuide:0x6000002d4a80'UIViewLayoutMarginsGuide'.trailing   (active)>",
    "<NSLayoutConstraint:0x600001890b90 UIView:0x7ff1d716e330.leading == UILayoutGuide:0x6000002d4620'UIViewLayoutMarginsGuide'.leading   (active)>",
    "<NSLayoutConstraint:0x600001890be0 UIView:0x7ff1d716e330.trailing == UILayoutGuide:0x6000002d4620'UIViewLayoutMarginsGuide'.trailing   (active)>",
    "<NSLayoutConstraint:0x600001891ae0 '_UITemporaryLayoutWidth' Mocha.CategoryHeaderBar:0x7ff1d71687d0.width == 0   (active)>",
    "<NSLayoutConstraint:0x600001890e10 'UIView-leftMargin-guide-constraint' H:|-(15)-[UILayoutGuide:0x6000002d4a80'UIViewLayoutMarginsGuide'](LTR)   (active, names: '|':UIView:0x7ff1d716e330 )>",
    "<NSLayoutConstraint:0x600001890aa0 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x6000002d4620'UIViewLayoutMarginsGuide'](LTR)   (active, names: '|':Mocha.CategoryHeaderBar:0x7ff1d71687d0 )>",
    "<NSLayoutConstraint:0x600001890eb0 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x6000002d4a80'UIViewLayoutMarginsGuide']-(15)-|(LTR)   (active, names: '|':UIView:0x7ff1d716e330 )>",
    "<NSLayoutConstraint:0x600001890b40 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x6000002d4620'UIViewLayoutMarginsGuide']-(0)-|(LTR)   (active, names: '|':Mocha.CategoryHeaderBar:0x7ff1d71687d0 )>"
)

I double check my code, all looks good. The problem is _UITemporaryLayoutWidth is automatically added by UIKit. Searching StackOverflow doesn’t give me anything immediately helpful.

So I checked Twitter and found _UITemporaryLayoutWidth is fucked as early as 2017:

One thing I learned is to search _UITemporaryLayoutWidth with Github Search. I had the luck this time to found two identical issues, from which I learned the reason why UITemporaryLayoutWidth is added.

In short, UITemporaryLayoutWidth is added when layoutIfNeeded is called before layoutSubviews. I call layoutIfNeeded in one of my UIControl class.

I call it because this control will do an animation by layouting subViews.

func toggleSelected(selected: Bool, _ animated: Bool) {
    self.isSelected = selected
    ...
    self.layoutIfNeeded()
}

To fix this, only do the layout when the view has been added to a UIWindow.

func toggleSelected(selected: Bool, _ animated: Bool) {
    self.isSelected = selected
    ...
    if self.window != nil {
      self.layoutIfNeeded()
    }
}
Posted 2020-04-17

More writing at jakehao.com