Edited at

iOSのAutoLayoutにおけるIntrinsic Content Sizeについて


はじめに

本記事ではiOSのAutoLayoutを理解するのに欠かせない、「Intrinsic Content Size」についての解説を行います。SwiftUIが話題になっていますが、ここ数年は既存のAutoLayoutを前提としたレイアウトもまだまだ多いと考えられるため、知っておいて損はない情報かと思います。


Intrinsic Content Sizeとは

Intrinsicはあまり聞き慣れない英単語ですが、「固有の」、「本来備わっている」といった意味を持ち、あるビューのコンテンツを表示するための最低のサイズを表します。

例えば、UILabelでは表示する文字列やフォントで表示する固有のサイズが決まり、UIImageViewでは表示する画像のサイズで表示する固有のサイズが決まります。

AutoLayoutを利用したレイアウトの場合、Intrinsic Content Sizeを考慮してレイアウト計算が行われます。これによって表示コンテンツのサイズを個別に指定せずともうまく表示される仕組みになっています。逆にもうまくいかないこともあるため、Intrinsic Content Sizeがどのように働くかを理解しておく必要があります。


Intrinsic Content SizeはUIViewクラスのプロパティである

UIViewクラスにはintrinsicContentSizeというIntrinsic Content Sizeを表すプロパティがあります。

var intrinsicContentSize: CGSize { get }

https://developer.apple.com/documentation/uikit/uiview/1622600-intrinsiccontentsize

このプロパティは読み取り専用のプロパティです。通常はintrinsicContentSizeを取得することにしか利用できませんが、UIViewのサブクラスでintrinsicContentSizeをオーバーライドすることでintrinsicContentSizeを変更することもできます。

例えば、内部に余白を持ったUILabelを作りたい場合は以下のように定義することができます。イイ感じのバッジUIをかんたんに作るためのTipsです。

// 内部に余白を持ったUILabel

public class PaddingLabel: UILabel {
// 余白のサイズ
public let padding: UIEdgeInsets = .zero

// テキストの描画範囲を余白のInsetを加えたものにする
public override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: padding))
}

// Intrinsic Content Sizeを余白のInset加えたものにする
public override var intrinsicContentSize: CGSize {
var intrinsicContentSize = super.intrinsicContentSize
intrinsicContentSize.height += padding.top + padding.bottom
intrinsicContentSize.width += padding.left + padding.right
return intrinsicContentSize
}
}

// 上下に4px、左右に8pxの余白を設定する

paddingLabel.padding = UIEdgeInsets(top: 4, left: 8, bottom: 4, right: 8)


Intrinsic Content SizeはStoryBoard/XIBで一時的に設定できる

Intrinsic Content SizeはStoryBoard/XIBで一時的に設定できます。この設定は実行時には影響しません。コンテンツはAPIから取得したりなど、動的に変化するものですが、StoryBoard/XIBでの暫定的なレイアウトを行うためにPlaceholderとして設定できるようです。

Intrinsic SizeをDefault(System Defined)からPlaceholderにすることで設定が可能。


Content Hugging PriorityとContent Compression Resistance Priority

Intrinsic Content Sizeに関連するものとして、Content Hugging PriorityContent Compression Resistance Priorityがあります。これらは略してCHCR Priorityとも呼ばれます。

Content Hugging Priority(コンテンツに沿う優先度)はコンテンツサイズよりも大きくなりにくさ、Content Compression Resistance Priority(コンテンツの圧縮抵抗優先度)はコンテンツサイズよりも小さくなりにくさを表す優先度です。

Content Compression Resistance Priorityは一律750がデフォルト値になりますが、Content Hugging Priorityは

Viewのサブクラスによって値が異なります。例えばUITextViewやUIButtonは250ですが、UILabelやUIImageViewは251です。さらに、UISwitchやUIActivityIndicatorViewなどコンテンツサイズよりも小さくすることができないクラスについてはContent Hugging Priorityは750になります。

クラス
Content Hugging Priority
Content Compression Resistance Priority

UITextView
250
750

UIButton
250
750

UILabel
251
750

UIImageView
251
750

UISwitch
750
750

UIActivityIndicatorView
750
750

制約(NSLayoutConstraint)のデフォルトの優先度は1000であるため、明示的にコンテンツサイズを制約で指定した場合は制約のコンテンツサイズが優先されます。

Content Hugging PriorityとContent Compression Resistance Priorityは、Viewにサイズを決定するための十分な制約がない場合にNSContentSizeLayoutConstraintという制約をつくるのに利用されます。

NSContentSizeLayoutConstraintは以下の制約となります。


  • ViewのサイズがintrinsicContentSize以下(優先度:Content Hugging Priorityの値)

  • ViewのサイズがintrinsicContentSize以上(優先度:Content Compression Resistance Priorityの値)

※NSContentSizeLayoutConstraintとNSLayoutConstraintの優先度が同じ場合はNSLayoutConstraintが優先されます。


まとめ


  • Intrinsic Content Sizeはあるビューのコンテンツを表示するための最低のサイズ

  • Intrinsic Content SizeはintrinsicContentSizeプロパティを継承することで設定可能

  • Viewのサイズを決定するための制約が不足している場合にIntrinsic Content SizeとContent Hugging PriorityとContent Compression Resistance Priorityの値を元に、NSContentSizeLayoutConstraintという制約が自動で設定される


参考