SwiftUI HStack等间距填充整个宽度

116

我有一个 HStack:

struct BottomList: View {
    var body: some View {
        HStack() {
            ForEach(navData) { item in
                NavItem(image: item.icon, title: item.title)
            }
        }
    }
}

如何使其内容完美居中,等距离地自动填满整个宽度?

顺便提一下,就像Bootstrap的CSS类.justify-content-around一样。

7个回答

213

使用 .infinity 作为 maxWidth 参数的 frame 布局修饰符可以实现此目的,无需额外使用 Shape 视图。

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)
            }
        }
    }
    }
}

使用.frame修饰符进行比较


25

在每个项目后面我插入了Spacer()...但对于最后一个项目,不要添加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()
                }
            }
        }
    }
}

即使项目的宽度不同,也可以均匀分布的示例列表: enter image description here

(注:接受的答案.frame(maxWidth: .infinity)并不适用于所有情况:在涉及具有不同宽度的项目时,它对我不起作用)


6
虽然我不是很喜欢这样做,但是使用.frame(maxWidth: .infinity)并没有给我想要的效果,因为它会将for each中每个对象的框架都放大,而不是保持它们定义的大小。这增加了我所寻求的对象之间的间距。 - Trevor

18
各种*Stack类型会尝试缩小到最小的大小来容纳其子视图。如果子视图有理想的大小,则*Stack不会扩展以填充屏幕。这可以通过将每个子视图放在ZStack中的透明Rectangle上来解决,因为Shape将尽可能地扩展。一种方便的方法是通过对View的扩展来实现:
extension View {
    func inExpandingRectangle() -> some View {
        ZStack {
            Rectangle()
                .fill(Color.clear)
            self
        }
    }
}

你可以像这样调用它:
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)
                }
            }

        }
    }
}

您可以根据需要调整HStack上的间距。

Non-expanding and expanding stacks


5
不需要额外的形状,只需为拥有.infinity值的子视图添加.frame修饰符,以便其扩展到填充父视图的宽度。请查看@svarrall的答案。 - Dot Freelancer

13

如果项目支持全宽度,它将自动完成,您可以在间隔符之间包装项目以实现此目的:

struct Resizable: View {
    let text: String

    var body: some View {
        HStack {
            Spacer()
            Text(text)
            Spacer()
        }
    }
}

SingleView

因此,您可以像这样在循环中使用它:

HStack {
    ForEach(data, id: \.self) { item in
        Resizable(text: item)
    }
}

MultiView


2
一个改进的方法是:struct Resizable: View { let content: () -> Content var body: some View { HStack { Spacer() content() Spacer() } } } - Ugo

7
你可以在堆栈中使用 spacing ... 以调整元素之间的间距。
HStack(spacing: 30){
            
            Image("NetflixLogo")
                .resizable()
                .scaledToFit()
                .frame(width: 40)
            
            Text("TV Show")
            Text("Movies")
            Text("My List")
            
        }
.frame(maxWidth: .infinity)

输出结果看起来像这样...

输入图像描述


5
如果你的数组有重复的值,使用array.indices来忽略最后一个元素后面的空格。
HStack() {
  ForEach(data.indices) { i in
    Text("\(data[i])")
    if i != data.last {
       Spacer()
    }
  }
}

3
我对接受的答案不满意,因为它们在Hstack的前后两侧留下了额外的“伪填充”。
这是我的解决方案示例:
struct EvenlySpacedHStack: View {
    let items: [Int] = [1,2,3]
    var body: some View {
        ZStack() {
            Capsule().foregroundColor(.yellow)
            HStack() {
                ForEach(Array(items.enumerated()), id: \.element) { index, element in
                    
                    switch index {
                    case items.count - 1:
                        Image(systemName: "\(element).circle.fill")
                            .resizable()
                            .aspectRatio(1.0, contentMode: .fit)
                    default:
                        Image(systemName: "\(element).circle.fill")
                            .resizable()
                            .aspectRatio(1.0, contentMode: .fit)
                        Spacer()
                    }
                }
            }
            .padding(8)
        }
        .frame(height: 55)
    }
}

这样的结果是:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接