在 SwiftUI 中让 VStack 填满屏幕宽度
Make a VStack fill the width of the screen in SwiftUI
鉴于此代码:
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
结果是这个界面:
即使 labels/text 组件不需要全宽,我怎样才能让 VStack
填满屏幕的宽度?
我发现的一个技巧是在结构中插入一个 empty HStack
,如下所示:
VStack(alignment: .leading) {
HStack {
Spacer()
}
Text("Title")
.font(.title)
Text("Content")
.lineLimit(nil)
.font(.body)
Spacer()
}
产生所需的设计:
有没有更好的方法?
尝试使用带有以下选项的 .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)
}
}
这被描述为一个灵活的框架 (see the documentation),它将伸展以填满整个屏幕,当它有额外的 space 时,它会将其内容居中。
有更好的方法!
要使 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)
}
}
您可以使用 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)
}
}
}
}
你的输出像:
另一种选择是将其中一个子视图放在 HStack
中,然后在其后放置 Spacer()
:
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)
}
}
导致:
另一种有效且可能更直观的堆叠排列如下:
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)
}
}
内容也可以很容易地 re-positioned,如有必要,只需删除 Spacer()
。
这对我有用(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
Result
使用这个
.edgesIgnoringSafeArea(.all)
我设法解决问题的最简单方法是使用 ZStack + .edgesIgnoringSafeArea(.all)
struct TestView : View {
var body: some View {
ZStack() {
Color.yellow.edgesIgnoringSafeArea(.all)
VStack {
Text("Hello World")
}
}
}
}
我知道这并不适用于所有人,但我觉得有趣的是,只需添加一个分隔线就可以解决这个问题。
struct DividerTest: View {
var body: some View {
VStack(alignment: .leading) {
Text("Foo")
Text("Bar")
Divider()
}.background(Color.red)
}
}
这是一段有用的代码:
extension View {
func expandable () -> some View {
ZStack {
Color.clear
self
}
}
}
比较使用和不使用 .expandable()
修饰符的结果:
Text("hello")
.background(Color.blue)
-
Text("hello")
.expandable()
.background(Color.blue)
您可以在方便的扩展中使用 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)
}
}
(注意不再需要垫片)
登录页面设计使用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()
}
}
使用 Swift 5.2 和 iOS 13.4,根据您的需要,您可以使用以下示例之一将您的 VStack
与顶部前导约束和全尺寸框架对齐.
请注意,下面的所有代码片段都会导致相同的显示,但不保证 VStack
的有效框架,也不保证在调试视图层次结构时可能出现的 View
元素的数量.
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)
}
}
2。使用 Spacer
s 强制对齐
您可以将 VStack
嵌入全尺寸 HStack
并使用尾部和底部 Spacer
强制您的 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
具有以下 declaration:
A container view that defines its content as a function of its own size and coordinate space. [...] This view returns a flexible preferred size to its parent layout.
下面的代码片段显示了如何使用 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:)
方法
如果您想在现有全尺寸 View
的基础上将您的 VStack
与顶部前导约束对齐,您可以使用 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
)
}
}
显示:
一个很好的解决方案,没有“装置”是被遗忘的 ZStack
ZStack(alignment: .top){
Color.red
VStack{
Text("Hello World").font(.title)
Text("Another").font(.body)
}
}
结果:
这是另一种可以节省项目时间的方法:
与其他不可重用的答案相比,代码少得多且可重用!
extension View {
var maxedOut: some View {
return Color.clear
.overlay(self, alignment: .center)
}
func maxedOut(color: Color = Color.clear, alignment: Alignment = Alignment.center) -> some View {
return color
.overlay(self, alignment: alignment)
}
}
用例:
struct ContentView: View {
var body: some View {
Text("Hello, World!")
.maxedOut
.background(Color.blue)
Text("Hello, World!")
.maxedOut(color: Color.red)
}
}
方式 1 -> 使用 MaxWidth 和 MaxHeight
import SwiftUI
struct SomeView: View {
var body: some View {
VStack {
Text("Hello, World!")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.red)
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
方式 2 -> 使用主屏幕边界
import SwiftUI
struct SomeView: View {
var body: some View {
VStack {
Text("Hello, World!")
}
.frame(maxWidth: UIScreen.main.bounds.width, maxHeight: UIScreen.main.bounds.height)
.background(.red)
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
方式 3 -> 使用几何 Reader
import SwiftUI
struct SomeView: View {
var body: some View {
GeometryReader { geometryReader in
VStack {
Text("Hello, World!")
}
.frame(maxWidth: geometryReader.size.width, maxHeight: geometryReader.size.height)
.background(.red)
}
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
方式 4 -> 使用垫片
import SwiftUI
struct SomeView: View {
var body: some View {
VStack {
Text("Hello, World!")
HStack{
Spacer()
}
Spacer()
}
.background(.red)
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
⚠️重要提示!
所有其他解决方案只是在周围添加一个框架内容!
✅ 但此解决方案改变了 实际框架 !
简单正确的扩展
你可以使用这个修饰符
.flexible(width: true, height: false)
演示
请注意内容如何完全按照您在原始堆栈中分配的方式对齐
这背后的代码(FlexibleViewModifier.swift)
extension View {
func flexible(width: Bool, height: Bool) -> some View {
self.modifier(MatchingParentModifier(width: width, height: height))
}
}
struct MatchingParentModifier: ViewModifier {
@State private var intrinsicSize: CGSize = UIScreen.main.bounds.size
private let intrinsicWidth: Bool
private let intrinsicHeight: Bool
init(width: Bool, height: Bool) {
intrinsicWidth = !width
intrinsicHeight = !height
}
func body(content: Content) -> some View {
GeometryReader { _ in
content.modifier(intrinsicSizeModifier(intrinsicSize: $intrinsicSize))
}
.frame(
maxWidth: intrinsicWidth ? intrinsicSize.width : nil,
maxHeight: intrinsicHeight ? intrinsicSize.height : nil
)
}
}
struct intrinsicSizeModifier: ViewModifier {
@Binding var intrinsicSize: CGSize
func body(content: Content) -> some View {
content.readIntrinsicContentSize(to: $intrinsicSize)
}
}
struct IntrinsicContentSizePreferenceKey: PreferenceKey {
static let defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
extension View {
func readIntrinsicContentSize(to size: Binding<CGSize>) -> some View {
background(
GeometryReader {
Color.clear.preference(
key: IntrinsicContentSizePreferenceKey.self,
value: [=11=].size
)
}
)
.onPreferenceChange(IntrinsicContentSizePreferenceKey.self) {
size.wrappedValue = [=11=]
}
}
}
鉴于此代码:
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
结果是这个界面:
即使 labels/text 组件不需要全宽,我怎样才能让 VStack
填满屏幕的宽度?
我发现的一个技巧是在结构中插入一个 empty HStack
,如下所示:
VStack(alignment: .leading) {
HStack {
Spacer()
}
Text("Title")
.font(.title)
Text("Content")
.lineLimit(nil)
.font(.body)
Spacer()
}
产生所需的设计:
有没有更好的方法?
尝试使用带有以下选项的 .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)
}
}
这被描述为一个灵活的框架 (see the documentation),它将伸展以填满整个屏幕,当它有额外的 space 时,它会将其内容居中。
有更好的方法!
要使 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)
}
}
您可以使用 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)
}
}
}
}
你的输出像:
另一种选择是将其中一个子视图放在 HStack
中,然后在其后放置 Spacer()
:
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)
}
}
导致:
另一种有效且可能更直观的堆叠排列如下:
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)
}
}
内容也可以很容易地 re-positioned,如有必要,只需删除 Spacer()
。
这对我有用(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
Result
使用这个
.edgesIgnoringSafeArea(.all)
我设法解决问题的最简单方法是使用 ZStack + .edgesIgnoringSafeArea(.all)
struct TestView : View {
var body: some View {
ZStack() {
Color.yellow.edgesIgnoringSafeArea(.all)
VStack {
Text("Hello World")
}
}
}
}
我知道这并不适用于所有人,但我觉得有趣的是,只需添加一个分隔线就可以解决这个问题。
struct DividerTest: View {
var body: some View {
VStack(alignment: .leading) {
Text("Foo")
Text("Bar")
Divider()
}.background(Color.red)
}
}
这是一段有用的代码:
extension View {
func expandable () -> some View {
ZStack {
Color.clear
self
}
}
}
比较使用和不使用 .expandable()
修饰符的结果:
Text("hello")
.background(Color.blue)
-
Text("hello")
.expandable()
.background(Color.blue)
您可以在方便的扩展中使用 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)
}
}
(注意不再需要垫片)
登录页面设计使用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()
}
}
使用 Swift 5.2 和 iOS 13.4,根据您的需要,您可以使用以下示例之一将您的 VStack
与顶部前导约束和全尺寸框架对齐.
请注意,下面的所有代码片段都会导致相同的显示,但不保证 VStack
的有效框架,也不保证在调试视图层次结构时可能出现的 View
元素的数量.
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)
}
}
2。使用 Spacer
s 强制对齐
您可以将 VStack
嵌入全尺寸 HStack
并使用尾部和底部 Spacer
强制您的 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
具有以下 declaration:
A container view that defines its content as a function of its own size and coordinate space. [...] This view returns a flexible preferred size to its parent layout.
下面的代码片段显示了如何使用 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:)
方法
如果您想在现有全尺寸 View
的基础上将您的 VStack
与顶部前导约束对齐,您可以使用 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
)
}
}
显示:
一个很好的解决方案,没有“装置”是被遗忘的 ZStack
ZStack(alignment: .top){
Color.red
VStack{
Text("Hello World").font(.title)
Text("Another").font(.body)
}
}
结果:
这是另一种可以节省项目时间的方法:
与其他不可重用的答案相比,代码少得多且可重用!
extension View {
var maxedOut: some View {
return Color.clear
.overlay(self, alignment: .center)
}
func maxedOut(color: Color = Color.clear, alignment: Alignment = Alignment.center) -> some View {
return color
.overlay(self, alignment: alignment)
}
}
用例:
struct ContentView: View {
var body: some View {
Text("Hello, World!")
.maxedOut
.background(Color.blue)
Text("Hello, World!")
.maxedOut(color: Color.red)
}
}
方式 1 -> 使用 MaxWidth 和 MaxHeight
import SwiftUI
struct SomeView: View {
var body: some View {
VStack {
Text("Hello, World!")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.red)
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
方式 2 -> 使用主屏幕边界
import SwiftUI
struct SomeView: View {
var body: some View {
VStack {
Text("Hello, World!")
}
.frame(maxWidth: UIScreen.main.bounds.width, maxHeight: UIScreen.main.bounds.height)
.background(.red)
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
方式 3 -> 使用几何 Reader
import SwiftUI
struct SomeView: View {
var body: some View {
GeometryReader { geometryReader in
VStack {
Text("Hello, World!")
}
.frame(maxWidth: geometryReader.size.width, maxHeight: geometryReader.size.height)
.background(.red)
}
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
方式 4 -> 使用垫片
import SwiftUI
struct SomeView: View {
var body: some View {
VStack {
Text("Hello, World!")
HStack{
Spacer()
}
Spacer()
}
.background(.red)
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
⚠️重要提示!
所有其他解决方案只是在周围添加一个框架内容!
✅ 但此解决方案改变了 实际框架 !
简单正确的扩展
你可以使用这个修饰符
.flexible(width: true, height: false)
演示
请注意内容如何完全按照您在原始堆栈中分配的方式对齐
这背后的代码(FlexibleViewModifier.swift)
extension View {
func flexible(width: Bool, height: Bool) -> some View {
self.modifier(MatchingParentModifier(width: width, height: height))
}
}
struct MatchingParentModifier: ViewModifier {
@State private var intrinsicSize: CGSize = UIScreen.main.bounds.size
private let intrinsicWidth: Bool
private let intrinsicHeight: Bool
init(width: Bool, height: Bool) {
intrinsicWidth = !width
intrinsicHeight = !height
}
func body(content: Content) -> some View {
GeometryReader { _ in
content.modifier(intrinsicSizeModifier(intrinsicSize: $intrinsicSize))
}
.frame(
maxWidth: intrinsicWidth ? intrinsicSize.width : nil,
maxHeight: intrinsicHeight ? intrinsicSize.height : nil
)
}
}
struct intrinsicSizeModifier: ViewModifier {
@Binding var intrinsicSize: CGSize
func body(content: Content) -> some View {
content.readIntrinsicContentSize(to: $intrinsicSize)
}
}
struct IntrinsicContentSizePreferenceKey: PreferenceKey {
static let defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
extension View {
func readIntrinsicContentSize(to size: Binding<CGSize>) -> some View {
background(
GeometryReader {
Color.clear.preference(
key: IntrinsicContentSizePreferenceKey.self,
value: [=11=].size
)
}
)
.onPreferenceChange(IntrinsicContentSizePreferenceKey.self) {
size.wrappedValue = [=11=]
}
}
}