SwiftUI HStack fill whole width with equal spacing
I have an HStack:
struct BottomList: View {
var body: some View {
HStack() {
ForEach(navData) { item in
NavItem(image: item.icon, title: item.title)
}
}
}
}
How do I perfectly center its content with equal spacing automatically filling the whole width?
FYI just like Bootstraps CSS class .justify-content-around
Solution 1:
The frame
layout modifier, with .infinity
for the maxWidth
parameter can be used to achieve this, without the need for an additional Shape
View.
struct ContentView: View {
var data = ["View", "V", "View Long"]
var body: some View {
VStack {
// This will be as small as possible to fit the data
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.border(Color.red)
}
}
// The frame modifier allows the view to expand horizontally
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.frame(maxWidth: .infinity)
.border(Color.red)
}
}
}
}
}
Solution 2:
The various *Stack
types will try to shrink to the smallest size possible to contain their child views. If the child view has an ideal size, then the *Stack
will not expand to fill the screen. This can be overcome by placing each child on top of a clear Rectangle
in a ZStack
, because a Shape
will expand as much as possible. A convenient way to do this is via an extension on View
:
extension View {
func inExpandingRectangle() -> some View {
ZStack {
Rectangle()
.fill(Color.clear)
self
}
}
}
You can then call it like this:
struct ContentView: View {
var data = ["View", "View", "View"]
var body: some View {
VStack {
// This will be as small as possible to fit the items
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.border(Color.red)
}
}
// Each item's invisible Rectangle forces it to expand
// The .fixedSize modifier prevents expansion in the vertical direction
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.inExpandingRectangle()
.fixedSize(horizontal: false, vertical: true)
.border(Color.red)
}
}
}
}
}
You can adjust the spacing on the HStack
as desired.
Solution 3:
I inserted Spacer()
after each item...but for the LAST item, do NOT add a Spacer()
:
struct BottomList: View {
var body: some View {
HStack() {
ForEach(data) { item in
Item(title: item.title)
if item != data.last { // match everything but the last
Spacer()
}
}
}
}
}
Example list that is evenly spaced out even when item widths are different:
(Note: The accepted answers .frame(maxWidth: .infinity)
did not work for all cases: it did not work for me when it came to items that have different widths)
Solution 4:
If items are fullwidth compatible, it will be done automatically, you can wrap items between spacers to make it happen:
struct Resizable: View {
let text: String
var body: some View {
HStack {
Spacer()
Text(text)
Spacer()
}
}
}
So you. can use it in a loop like:
HStack {
ForEach(data, id: \.self) { item in
Resizable(text: item)
}
}
Solution 5:
You can also use spacing
in stacks ... ie
HStack(spacing: 30){
Image("NetflixLogo")
.resizable()
.scaledToFit()
.frame(width: 40)
Text("TV Show")
Text("Movies")
Text("My List")
}
.frame(maxWidth: .infinity)
output result looks like this ...