Implementing a Custom View to Work with Auto Layout

Auto Layout reduces the burden on controller classes by making views more self-organizing. If you implement a custom view class, you must provide enough information so that the Auto Layout system can make the correct calculations and satisfy the constraints. In particular, you should determine whether the view has an intrinsic size, and if so, implement intrinsicContentSize to return a suitable value.

A View Specifies Its Intrinsic Content Size

Leaf-level views, such as buttons, typically know more about what size they should be than does the code that is positioning them. This is communicated through the method intrinsicContentSize, which tells the layout system that there is some content it doesn’t natively understand in a view, and which provides to the layout system the intrinsic size of that content.

A typical example is a single-line text field. The layout system does not understand text—it must just be told that there’s something in the view, and that that something will require a certain amount of space in order not to clip the content. The layout system calls intrinsicContentSize, and sets up constraints that specify (1) that the opaque content should not be compressed or clipped and (2) that the view prefers to hug tightly to its content.

A view can implement intrinsicContentSize to return absolute values for its width and height, or to return NSViewNoInstrinsicMetric for either or both to indicate that it has no intrinsic metric for a given dimension.

For further examples, consider the following implementations of intrinsicContentSize:

Views Must Notify Auto Layout If Their Intrinsic Size Changes

If any property of your view changes, and that change affects the intrinsic content size, your view must call invalidateIntrinsicContentSize so that the layout system notices the change and can recalculate the layout. For example, a text field calls invalidateIntrinsicContentSize if the string value changes.

Layout Operates on Alignment Rectangles, Not on Frames

When considering layout, keep in mind that the frame of a control is less important than its visual extent. As a result, ornaments such as shadows and engraving lines should typically be ignored for the purposes of layout. Interface Builder ignores them when positioning views on the canvas—in the following example, the Aqua guide (the dashed blue line) aligns with the visual extent of the button, not with the button’s frame (the solid blue rectangle).

../Art/buttonGuideFrame.png

To allow layout based on the presentation of the content rather than the frame, views provide an alignment rectangle, which is what the layout actually operates on. To determine whether your override is correct on OS X, you can set the NSViewShowAlignmentRects default to YES to draw the alignment rects.

Views Indicate Baseline Offsets

Frequently, you want to align controls by baseline. Although you have always been able to align against baselines in Interface Builder, it was not previously possible to do this programatically. You can do so now using NSLayoutAttributeBaseline in a constraint.

The method baselineOffsetFromBottom returns the distance between NSLayoutAttributeBottom and NSLayoutAttributeBaseline. This default return value for NSView is 0; subclasses override this method when it makes sense to do so.