What is intrinsic content size and why care?

In this post I would like to touch a bit on a less known part of UIKit layout. The what and why of intrinsic content size.

Published: Jan. 27, 2021
App Store

I am sure you noticed if you ever build UI with UIKit that with controls like UIButton or UILabel, you need fewer constraints than for, say a UIStackView or plain UIView. You can center UILabel vertically and horizontally, and the layout is completely defined as AutoLayout is concerned. This will not work with UIView, and the reason has all to do with intrinsic content size.

Intrinsic content size explained

I have to confess that the "intrinsic" part is not intuitive for me as a non-native English speaker, and looking up definitions does not help either. However, for UIKit purposes, we can redefine intrinsic content size as "ideal content size".

Intrinsic size is the size the UI control in question would like to occupy. This makes a lot of sense for UILabel since its size is based on its textual content. The same goes for UIButton. These controls have some sense of how much screen space they need to work correctly.

So if a control has an intrinsic content size, it means it will occupy this size unless told otherwise.

I hope this explains the need for fewer constraints when working with labels and buttons. And now, let's turn to the more practical size.

Practical applications

Why even care? Because you can specify the intrinsic content size for your controls. This is great because you don't have to set up all the constraints, and you can communicate your intended size.

For example, if you are building a custom base subclass for your buttons. Which you should do to achieve visual consistency and have easier time changing your design. You can specify the intrinsic height to be at least 44 points, and now you need to think about this when working with these buttons.

44 points is Apple's recommended height for touch targets even though the vanilla UIButton ignores this, and its intrinsic height is 30 points.

This could look something like this:

class BaseButton: UIButton {
    override var intrinsicContentSize: CGSize {
        let superSize = super.intrinsicContentSize
        return CGSize(width: superSize.width, height: 44)

And now, each button (assuming you use this BaseButton class) is at least 44 points tall without specifying the constraint.

I should also mention that this intrinsic size is not trivial to compute. Button has various kinds of internal insets the programmer can configure, so when you can, you should first get the super intrinsic size and modify it as needed.

Another use case for intrinsic content size would be when creating custom separators. You can specify an intrinsic height of, say 0.5 points which means you don't have to remember to set this height via constraint each time you create the separator.

Only intrinsic height or width?

What about controls where only width or height makes sense for the intrinsic size? Don't worry. UIKit has got you covered.

There is noIntrinsicMetric static property on the UIView.

So for our hypothetical separator, we would use it like this:

class Separator: UIView {
    override var intrinsicContentSize: CGSize {
        return CGSize(width: UIView.noIntrinsicMetric, height: 0.5)

Communicating intrinsic size changes

Suppose you have a custom control that has variable intrinsic size. Maybe because you are calculating it based on the configuration of the subviews, there is an easy way to inform the layout system that the intrinsic case has changed.

Each UIView instance has invalidateIntrinsicContentSize() method used just for this purpose. So if your custom UIView subclass has, for example UILabel, which it uses to calculate the intrinsic content size, and you set new text, you should call invalidateIntrinsicContentSize to make sure your layout gets updated correctly.

Filip Němeček profile photo


Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀