在SwiftUI中使VStack充满屏幕的宽度


119

给定此代码:

import SwiftUI

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)

            Text("Content")
                .lineLimit(nil)
                .font(.body)

            Spacer()
        }
        .background(Color.red)
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

导致这种交互:

预习

VStack即使标签/文本组件不需要整个宽度,也如何填充屏幕的宽度?

我发现的一个技巧是在结构中插入一个 HStack,如下所示:

VStack(alignment: .leading) {
    HStack {
        Spacer()
    }
    Text("Title")
        .font(.title)

    Text("Content")
        .lineLimit(nil)
        .font(.body)

    Spacer()
}

产生所需的设计:

期望的输出

有没有更好的办法?


1
请参阅我的答案,其中显示了多达5种获得相似结果的替代方法。
Imanou Petit

Answers:


196

尝试将.frame修饰符与以下选项一起使用:

.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Hello World").font(.title)
            Text("Another").font(.body)
            Spacer()
        }.frame(minWidth: 0,
                maxWidth: .infinity,
                minHeight: 0,
                maxHeight: .infinity,
                alignment: .topLeading
        ).background(Color.red)
    }
}

这被描述为是一个灵活的框架(请参阅文档),它将伸展以填充整个屏幕,并且当它有多余的空间时,它将使其内容居中。


子视图到左不对齐(.leading)。如果放在之前,则采用完整宽度.background(Color.red)
ielyamani

添加了代码以使其也向左对齐。现在应该完全匹配
svarrall

12
错了 该.backgound(Color.red)是框架的观点的背景,但不是的背景VStack是在框架视图。(其原因已在WWDC视频:P中说明)。如果放在.background(Color.green)之前,.frame您会看到VStack静止的宽度适合其内容,而框架具有红色背景...当您放置时.frame(...),它并不编辑VStack,实际上是在创建另一个视图。
杰克

2
@杰克似乎是一场灾难的食谱
Sudara

1
有一个问题:为什么要使用alignment:.topLeading,因为我们有VStack(alignment:.leading)?感谢您的分享
UIChris

36

下面是一种可行且可能更直观的替代堆叠安排:

struct ContentView: View {
    var body: some View {
        HStack() {
            VStack(alignment: .leading) {
                Text("Hello World")
                    .font(.title)
                Text("Another")
                    .font(.body)
                Spacer()
            }
            Spacer()
        }.background(Color.red)
    }
}

Spacer()如果需要,也可以通过删除来轻松地重新定位内容。

添加/删除垫片以更改位置


4
alternative喜欢gif
ielyamani,

1
我发现这不仅更直观,而且更符合OP的问题,我的问题是关于如何让Stack元素填充其容器,而不是如何在元素周围插入一个容器。
brotskydotcom

1
我发现这是与SwiftUI背后的理念相吻合的最佳答案
krummens 20-4-29

1
绝对的,对于这种通用布局,这里的SwiftUI-y答案最为明确。
dead_can_dance

30

有一个更好的方法!

VStack填充其父对象的宽度,您可以使用GeometryReader并设置边框。(.relativeWidth(1.0)应该可以,但是现在看来不行)

struct ContentView : View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("test")
            }
                .frame(width: geometry.size.width,
                       height: nil,
                       alignment: .topLeading)
        }
    }
}

要设置VStack实际屏幕的宽度,可以UIScreen.main.bounds.width在设置框架时使用GeometryReader,而不是使用,但是我想您可能希望使用父视图的宽度。

此外,这种方式还有一个好处,VStack就是如果您向中添加了HStack带有的Spacer(),则不会在其中添加空格(如果您有间距)VStack

更新-没有更好的方法!

签出接受的答案后,我意识到接受的答案实际上不起作用!乍一看似乎可以使用,但是如果您将的VStack背景更新为绿色,则会发现VStack宽度仍然相同。

struct ContentView : View {
    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                Text("Hello World")
                    .font(.title)
                Text("Another")
                    .font(.body)
                Spacer()
                }
                .background(Color.green)
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
                .background(Color.red)
        }
    }
}

这是因为.frame(...)实际上是在视图层次结构中添加另一个视图,并且该视图最终填满了屏幕。但是,VStack仍然没有。

这个问题似乎在我的答案中也相同,可以使用与上述相同的方法进行检查(在之前和之后放置不同的背景颜色.frame(...)。似乎真正加宽的唯一方法VStack是使用间隔子:

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack{
                Text("Title")
                    .font(.title)
                Spacer()
            }
            Text("Content")
                .lineLimit(nil)
                .font(.body)
            Spacer()
        }
            .background(Color.green)
    }
}

尝试过relativeWidth,没有用。GeometryReader如果.frame放置在.background(Color.red)
某处,将以

GeometryReader当我在IShow SwiftUI Inspector之后或用户界面中的任何其他位置“添加修饰符”时,在显示的列表中看不到。你是怎么发现的GeometryReader

1
@gone-堆栈溢出:)
杰克

20

使用Swift 5.2和iOS 13.4,根据您的需要,您可以使用以下示例之一使您VStack与最主要的约束条件和全尺寸框架保持一致。

请注意,所有下面的代码段将导致相同的显示,但不能保证在调试视图层次结构时有效的帧框架VStackView可能出现的元素数量。


1.使用frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)方法

最简单的方法是设置您的框架的VStack最大宽度和高度,并在frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)以下位置传递所需的对齐方式:

struct ContentView: View {

    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)
            Text("Content")
                .font(.body)
        }
        .frame(
            maxWidth: .infinity,
            maxHeight: .infinity,
            alignment: .topLeading
        )
        .background(Color.red)
    }

}

作为替代方案,如果View在代码库中为s设置具有特定对齐方式的最大帧是常见的模式,则可以为其创建扩展方法View

extension View {

    func fullSize(alignment: Alignment = .center) -> some View {
        self.frame(
            maxWidth: .infinity,
            maxHeight: .infinity,
            alignment: alignment
        )
    }

}

struct ContentView : View {

    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)
            Text("Content")
                .font(.body)
        }
        .fullSize(alignment: .topLeading)
        .background(Color.red)
    }

}

2.使用Spacers强制对齐

您可以将其嵌入VStack完整尺寸的内部,HStack并使用tail和bottomSpacer来强制VStack顶部对齐:

struct ContentView: View {

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)

                Spacer() // VStack bottom spacer
            }

            Spacer() // HStack trailing spacer
        }
        .frame(
            maxWidth: .infinity,
            maxHeight: .infinity
        )
        .background(Color.red)
    }

}

3.使用ZStack和全尺寸背景View

本示例说明了如何将您的VStack内部嵌入ZStack顶部对齐的顶部。注意如何使用Color视图设置最大宽度和高度:

struct ContentView: View {

    var body: some View {
        ZStack(alignment: .topLeading) {
            Color.red
                .frame(maxWidth: .infinity, maxHeight: .infinity)

            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)
            }
        }
    }

}

4.使用 GeometryReader

GeometryReader具有以下声明

一个容器视图,根据其自身大小和坐标空间定义其内容。[...]此视图将灵活的首选大小返回其父布局。

以下代码段显示了如何GeometryReader使您VStack与最主要的约束条件和完整尺寸的框架对齐:

struct ContentView : View {

    var body: some View {
        GeometryReader { geometryProxy in
            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)
            }
            .frame(
                width: geometryProxy.size.width,
                height: geometryProxy.size.height,
                alignment: .topLeading
            )
        }
        .background(Color.red)
    }

}

5.使用overlay(_:alignment:)方法

如果要VStack在现有完整尺寸的基础上与最主要的约束条件保持一致View,可以使用以下overlay(_:alignment:)方法:

struct ContentView: View {

    var body: some View {
        Color.red
            .frame(
                maxWidth: .infinity,
                maxHeight: .infinity
            )
            .overlay(
                VStack(alignment: .leading) {
                    Text("Title")
                        .font(.title)
                    Text("Content")
                        .font(.body)
                },
                alignment: .topLeading
            )
    }

}

显示:


10

我设法解决此问题的最简单方法是使用ZStack + .edgesIgnoringSafeArea(.all)

struct TestView : View {

    var body: some View {
        ZStack() {
            Color.yellow.edgesIgnoringSafeArea(.all)
            VStack {
                Text("Hello World")
            }
        }
    }
}


4

您可以通过使用 GeometryReader

几何阅读器

码:

struct ContentView : View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
               Text("Turtle Rock").frame(width: geometry.size.width, height: geometry.size.height, alignment: .topLeading).background(Color.red)
            }
        }
    }
}

您的输出如下:

在此处输入图片说明


4

一个好的解决方案,没有“困扰”,是被遗忘的 ZStack

ZStack(alignment: .top){
    Color.red
    VStack{
        Text("Hello World").font(.title)
        Text("Another").font(.body)
    }
}

结果:

在此处输入图片说明


3

另一种选择是将一个子视图放在an内,HStack然后在Spacer()其后放置a :

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {

            HStack {
                Text("Title")
                    .font(.title)
                    .background(Color.yellow)
                Spacer()
            }

            Text("Content")
                .lineLimit(nil)
                .font(.body)
                .background(Color.blue)

            Spacer()
            }

            .background(Color.red)
    }
}

导致 :

VStack内的HStack


1
恕我直言,这是最优雅的解决方案
维亚切斯拉夫

3

这是对我有用的(ScrollView(可选),因此可以根据需要添加更多内容以及居中的内容):

import SwiftUI

struct SomeView: View {
    var body: some View {
        GeometryReader { geometry in
            ScrollView(Axis.Set.horizontal) {
                HStack(alignment: .center) {
                    ForEach(0..<8) { _ in
                        Text("🥳")
                    }
                }.frame(width: geometry.size.width, height: 50)
            }
        }
    }
}

// MARK: - Preview

#if DEBUG
struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        SomeView()
    }
}
#endif

结果

居中


2

我知道这对每个人都行不通,但是我觉得很有趣,仅添加一个Divider即可解决这个问题。

struct DividerTest: View {
var body: some View {
    VStack(alignment: .leading) {
        Text("Foo")
        Text("Bar")
        Divider()
    }.background(Color.red)
  }
}

2

这是一个有用的代码:

extension View {
    func expandable () -> some View {
        ZStack {
            Color.clear
            self
        }
    }
}

比较使用和不使用.expandable()修饰符的结果:

Text("hello")
    .background(Color.blue)

--

Text("hello")
    .expandable()
    .background(Color.blue)

在此处输入图片说明


2

在此处输入图片说明

使用登录页面设计 SwiftUI

import SwiftUI

struct ContentView: View {

    @State var email: String = "william@gmail.com"
    @State var password: String = ""
    @State static var labelTitle: String = ""


    var body: some View {
        VStack(alignment: .center){
            //Label
            Text("Login").font(.largeTitle).foregroundColor(.yellow).bold()
            //TextField
            TextField("Email", text: $email)
                .textContentType(.emailAddress)
                .foregroundColor(.blue)
                .frame(minHeight: 40)
                .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.green))

            TextField("Password", text: $password) //Placeholder
                .textContentType(.newPassword)
                .frame(minHeight: 40)
                .foregroundColor(.blue) // Text color
                .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.green))

            //Button
            Button(action: {

            }) {
                HStack {
                    Image(uiImage: UIImage(named: "Login")!)
                        .renderingMode(.original)
                        .font(.title)
                        .foregroundColor(.blue)
                    Text("Login")
                        .font(.title)
                        .foregroundColor(.white)
                }


                .font(.headline)
                .frame(minWidth: 0, maxWidth: .infinity)
                .background(LinearGradient(gradient: Gradient(colors: [Color("DarkGreen"), Color("LightGreen")]), startPoint: .leading, endPoint: .trailing))
                .cornerRadius(40)
                .padding(.horizontal, 20)

                .frame(width: 200, height: 50, alignment: .center)
            }
            Spacer()

        }.padding(10)

            .frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity, minHeight: 0, idealHeight: .infinity, maxHeight: .infinity, alignment: .top)
            .background(Color.gray)

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

1

您可以在方便的扩展程序中使用GeometryReader来填充父级

extension View {
    func fillParent(alignment:Alignment = .center) -> some View {
        return GeometryReader { geometry in
            self
                .frame(width: geometry.size.width,
                       height: geometry.size.height,
                       alignment: alignment)
        }
    }
}

因此,使用请求的示例,您将获得

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)

            Text("Content")
                .lineLimit(nil)
                .font(.body)
        }
        .fillParent(alignment:.topLeading)
        .background(Color.red)
    }
}

(请注意不再需要垫片)

在此处输入图片说明


这不是全屏。
鸭子

问题不是要求全屏显示,而是要求全屏显示。如果要超出安全区域,请使用。edgeIgnoringSafeArea
困惑的Vorlon

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.