SwiftUI の fixedSize で自然サイズを優先させる
SwiftUI のレイアウトシステムでは、親ビューが子ビューにサイズを提案し、子ビューはその提案に基づいて自身のサイズを決定します。fixedSize モディファイアはこの交渉プロセスに介入し、親の提案を無視して子ビューが本来持つ「理想サイズ」を優先させます。
テキストが切れる問題
fixedSize の必要性を理解するために、まずテキストが切り詰められるケースを見てみましょう。
HStack {
Text("これは非常に長いテキストで、親のスペースに収まりきらない場合があります")
.lineLimit(1)
Text("右端")
}
.frame(width: 250)HStack の幅が 250 に制限されているため、左側の Text は省略記号(…)で切り詰められます。通常はこの動作が望ましいですが、テキスト全体を表示したい場面もあるでしょう。
fixedSize で理想サイズを優先させる
fixedSize を付けると、ビューは親からのサイズ提案を無視して、自分のコンテンツに必要なサイズを確保します。
Text("これは非常に長いテキストで、親のスペースに収まりきらない場合があります")
.fixedSize()この Text は親が狭いスペースを提案しても、テキスト全体を表示するのに必要な幅を確保します。結果として親の領域をはみ出す可能性がありますが、テキストが切れることはなくなります。
水平・垂直を個別に制御する
fixedSize は水平方向と垂直方向を独立に制御できます。
Text("長いテキストが複数行にわたって表示されます。fixedSize で垂直方向の理想サイズを優先します。")
.fixedSize(horizontal: false, vertical: true)
.frame(width: 200)horizontal: false で幅は親の提案に従い、vertical: true で高さは理想サイズ(全テキストを表示するのに必要な高さ)を優先します。つまり、幅は制限しつつ行数は切り詰めないという動作になります。
幅は理想サイズを優先し、高さは親の提案に従う。テキストを 1 行で全部表示したいときに使う
幅は親の提案に従い、高さは理想サイズを優先する。テキストの行数を制限せず全文を表示したいときに使う
HStack 内での fixedSize
複数のビューがスペースを奪い合う状況で、特定のビューだけにサイズ優先権を与えたいときにも fixedSize は役立ちます。
HStack {
Text("ラベル")
.fixedSize()
Text("この部分は長いテキストですが、スペースに応じて折り返されます")
.lineLimit(2)
}
.padding()「ラベル」に fixedSize を付けることで、このテキストは絶対に省略されません。残りのスペースが右側の長いテキストに割り当てられ、そちらが折り返しや省略を受け持ちます。
複数ビューの高さを揃える
fixedSize の意外な活用法として、HStack 内の複数ビューの高さを揃えるパターンがあります。
HStack(alignment: .top) {
Text("短い")
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
Text("こちらは少し長いテキストで、複数行にわたります")
.padding()
.frame(maxWidth: .infinity)
.background(Color.green.opacity(0.2))
.cornerRadius(8)
}
.fixedSize(horizontal: false, vertical: true)HStack に .fixedSize(horizontal: false, vertical: true) を付けると、HStack は子ビューの中で最も高いものに合わせた理想の高さを確保します。これにより、カード型 UI で高さが揃わない問題を解決できます。
fixedSize を使うときの注意点
fixedSize はビューを親の領域からはみ出させる可能性があるため、使いどころを選ぶ必要があります。ScrollView の中やリストのセル内など、はみ出しても問題ない文脈で使うのが安全です。固定レイアウトの画面で無闇に fixedSize を使うと、他のビューと重なってしまう恐れがあるので注意しましょう。