Auto Layout, Custom Controls and layoutSubviews

Auto Layout is a descriptive, constraint-based view layout solver. This means that you describe the layout in Interface Builder or in code and the frames are then calculated from this description.

There are 3 phases to displaying a view:

  1. update constraints
  2. update layout
  3. update display

There are three things you should keep in mind when creating a custom control that behaves well with Auto Layout.

Overriding layoutSubviews

layoutSubviews is called in phase 2 of the list above. Constraints are updated top-down in the stack of views. But the layout is updated bottom-up.

When overriding layoutSubviews the invariant that frames agree with constraints must remain true. Therefore, your implementation of layoutSubviews will probably look like:

override func layoutSubviews() {
  super.layoutSubviews()

  // intrinsic content size changes

  super.layoutSubviews()
}

The first call to super.layoutSubviews() is required. This is where the framework updates the layout based on the constraints.

The second call to super.layoutSubviews() is optional but may be required if the intrinsic conent size of the view changes. A UIButton’s label text changing is an example of a change in intrinsic content size.

Animating layouts

In general, a views frame should not be messed with. The one exception is animation. You can animate frame properties or constraints.

UIView.animateWithDuration(0.2, animations: {
  // animate frame or constraints
}, completion: { _ in
  view.layoutIfNeeded()
})

layoutIfNeeded() should be called at the end of an animation because constraint solver pass may be required.

Animating constraints rather than frames has the advantage that the view will animate with the constaint-compliant layout. Animating the frame may have the advantage of better performance.

Profile your code.

Alignment Rects

Constraints operate on alignment rects. An alignment rect may be different from the frame rect. A view having a badge, shadow, or reflection is an example where the alignment rect would differ from the frame rect. Check the alignment rect if something looks off from what you expect.

func alignmentRectForFrame(_ frame: CGRect) -> CGRect
func frameForAlignmentRect(_ alignmentRect: CGRect) -> CGRect

Summary

Learn Auto Layout. It is the future.