[SwiftUI] Spacerはなぜいい感じに伸びてくれるのか
要約
SwiftUIでSpacer
を使うと、ViewとViewの間のスペースを自身が伸びたり縮んだりして調整してくれる。
その挙動を、layoutPriority
の点から解説する。
Spacer
とは
Spacerは、SwiftUIのView
のひとつで、HStack
やVStack
の中で使われることが多い。
たとえば、要素を均等な間隔で配置したり、中央に配置する場合に使用する。
Text
とSpacer
からなるHStack`
たとえば、2つのText
をHStack
で横に並べる場合、
HStack {
Text("Hello")
.border(.red)
Text("World")
.border(.blue)
}.padding()
.frame(width: 300)
.border(.green)
Spacer
を挟むことで、
Text
を両端に配置したり
HStack {
Text("Hello")
.border(.red)
Spacer()
Text("World")
.border(.blue)
}.padding()
.frame(width: 300)
.border(.green)
Text
を均等に配置したりできる。
HStack {
Spacer()
Text("Hello")
.border(.red)
Spacer()
Text("World")
.border(.blue)
Spacer()
}.padding()
.frame(width: 300)
.border(.green)
Color
とSpacer
からなるHStack
HStack {
Text("Hello")
.border(.red)
Spacer()
Color.blue
}
.frame(width: 300, height: 100)
.border(.green)
Color
とText
の間にSpacer
を挟むと、Spacer
が縮み、Color
が最大まで大きくなる。
Text
+ Spacer
+ Text
の場合は、Spacer
が良い感じに伸びていて、
Text
+ Spacer
+ Color
の場合は、Spacer
が良い感じに縮んでいるように感じる。
これらの挙動は、すべてSpacer
のlayoutpriority
の値によるものである。
LayoutPriority
とは
SpacerのサイズとLayoutPriority
Spacerには、以下のサイズとlayoutpriorityが設定されている。
- minWidth:
8
, minHeight:8
← どんなに小さくても、このサイズになる。 - idealWidth:
8
, idealHeight:8
← 特に制約がなければ、このサイズになろうとする。 - maxWidth:
inf
, maxHeight:inf
← 広がれるなら、どこまでも広がる。 - layoutPriority:
-inf
← ほかのどんなViewよりも、余ったスペースに広がる優先度が低い。
挙動の説明
これを踏まえると、上記の挙動が理解できる。
ポイントとして、
-
HStack
ではlayoutPriority
が大きいViewから余ったスペースが与えられる。(ref: https://zenn.dev/ueeek/articles/20250125my_hstack) -
LayoutPriority
を指定していないViewは、layoutPriority
が0
となる。
Text
+ Spacer
+ Text
の場合
Text
は、maxWidth
が指定されていて、Hello
は39
、World
は45
程度である。
そのため、width=300
のHStack
のうち、39 + 45
の84
がText
に割り当てられ、216
が余る。
余ったスペースは、ほかにpriority
が高いViewが存在しないため、Spacer
に割り当てられる。
これにより、Spacer
が余ったスペースに広がり、Text
が両端に配置される。
Color
+ Spacer
+ Text
の場合
Text
は、maxWidth
が指定されていて、Hello
は39
程度である。
Color
には、maxWidth
がinf
で指定されているため、どこまでも広がることができる。
余ったスペースを埋めることができるViewのうち(Color, Spacer)、一番layoutPriority
が高いのはColor
である。
Text
の39
とSpacer
のminWidth
である8
を除いた部分が、Color
に割り当てられる。
そのため、Color
が最大まで広がり、Spacer
が縮む。
まとめ
Spacer
は、layoutPriority = -inf
、minWidth = 8
、minHeight = 8
をもつViewである。
HStack
の中のViewは、layoutPriority
が高い順に余ったスペースが与えられる。
これらより、Spacer
の挙動としては、
-
maxWidth
が固定のViewなどと一緒にHStack
に入れたときは、余ったスペースを埋めるように広がる。(e.g. icon, Text) -
maxWidth
がinf
のViewなどと一緒にHStack
に入れたときは、layoutPriority
の低さから、広がることができずに縮む。(e.g.frame()
を指定してない、ColorやRectangle)
余談: LayoutPriorityや、min/ideal/max Sizeを調べた方法
Layout
Protocolで使用される、Subview.sizeThatFits
で、min/ideal/max
の値を確認できる。
struct TestLayout: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
print("# minSize \(subviews[0].sizeThatFits(.zero))")
print("# idealSize \(subviews[0].sizeThatFits(.unspecified))")
print("# maxSize \(subviews[0].sizeThatFits(.infinity))")
print("# layoutPriority \(subviews[0].priority)")
return .zero
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
// Empty
}
}
#Preview {
TestLayout {
Text("Hello")
}
}
Discussion