Trouble with AutoLayout on UITableViewCell
A few things to cover here:
-
The
NSIBPrototypingLayoutConstraint
constraints that you're running into (and that are causing exceptions) are auto-generated by Interface Builder in order to make your Storyboard or XIB view layout non-ambiguous. It's pretty sneaky about doing this, but it's automatically adding the minimum constraints required so that the position and size of each ambiguous view becomes fully specified. This is a change from Xcode 4, because in Xcode 4 you could not have ambiguous layouts in Interface Builder. With Xcode 5 and later you can, however IB will auto-generate these constraints for you if your layout is ambiguous at compile time.The way to fix this issue is to add the minimum required constraints in Interface Builder so that each view's position & size is fully specified, then select each of these unwanted constraints, go to the right sidebar Attributes inspector, and check the box next to Placeholder - Remove at build time.
Not only does this checkbox remove the constraint you added, but most importantly it will prevent the auto-generated IB constraint from taking its place! (As you can imagine, this is quite tedious when you have a number of views in IB and want to manage all your constraints in code. For this reason you may want to avoid using IB entirely for view hierarchies in which you intend to implement Auto Layout programmatically.)
What is the difference between a Placeholder constraint and an Uninstalled constraint? Here's a slide from my Adaptive Auto Layout talk (video) (PDF slides) comparing the two:
In
updateConstraints
, you don't want to remove constraints and re-add them like you have there. Why not? Essentially, it's terrible for performance, and I have confirmed with Apple engineers that this is not a good idea. See the question/answer I have posted here for some more details, as well as this answer. In order to prevent constraints being added more than once, use a boolean flag (e.g.hasSetupConstraints
) that you set to YES once you have set up your constraints the first time, and ifupdateConstraints
is called again you can just return immediately if you have no new constraints to add. See this question for further discussion.The code you're using to remove constraints may not work completely. This is because
[view removeConstraints:view.constraints]
will only remove constraints that have been added toview
-- remember that constraints can be added to any common superview of the views they constrain -- and the constraints added toview
may not be the only ones affecting the layout ofview
! If you need to remove a number of constraints, you should store a reference to each of those constraints in a property (e.g. an NSArray property containing NSLayoutConstraint instances), and then deactivate/remove those constraints using the API on NSLayoutConstraint or the PureLayout open-source library. You should only deactivate/remove as few constraints as possible because it is computationally expensive to do so. On the other hand, changing theconstant
of any constraint is very efficient and encouraged, and you don't need to remove or re-add the constraint to do that.