VStack、HStack、ZStack 实际上都是 @ViewBuilder,他们是返回 view 的函数,不允许为空。ViewBuilder 不绘制任何东西,它只是将不同的View组合到一起;但是加到 ViewBuilder 的修饰符,都会传递到它所包含的视图中去。关于 ViewBuilder 详细说明,请查看:‣
// alignment 和 spacing 可以设置对齐和间距
VStack(alignment: .leading, spacing: 10){
Text("Choose")
Text("Your Plan")
}
// 如果 VStack 堆栈中只有一个视图,并且不带任何参数,则可以将视图的初始值设定项直接传递给 VStack 以使代码更短:
if sizeClass == .compact {
VStack(content: UserView.init)
} else {
HStack(content: UserView.init)
}
HStack 和 VStack 类似,但有一些需要注意的:
//等分内部区块:通过设置 maxWidth: .infinity, 保持不同区块的大小一致
HStack{
VStack{}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100)
VStack{}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100)
}.padding(.horizontal)
//对齐第一个/最后一个单词
HStack(alignment: .firstTextBaseline){...}
HStack(alignment: .lastTextBaseline){...}
ZStack 和 VStack和HStack一样,都是收缩视图,但有时它会被内部的 Color 视图撑开,让人误以为是扩张型视图。
ZStack 没有间距的概念,因为视图重叠,但它确实对齐。ZStack 的对齐是跟元素中,最大的那个元素对齐;所以当有一个大东西 ZStack 和一个小东西,你可以使两个视图都对齐到顶部
绝对定位的视图,都可以用ZStack来实现
ZStack(alignment: .topLeading) {...}
// 调整某个具体元素的位置
ZStack{
Text("Copywriting")
.offset(x:0,y:80)
}
// 把 ZStack 扩张到整个手机屏幕
ZStack{}
.edgesIgnoringSafeArea(.all)
// 如果不希望内容也扩张到安全区域外,可以只给背景视图加忽略标记,而不要在ZStack里加
ZStack {
Color.gray
.edgesIgnoringSafeArea(.all)
...
}
LazyVStack
& LazyHStack
被称为惰性堆栈。之前 scrollView
里面的子元素,都是一开始就全部添加进视图的,即使在屏幕上看不见。SwiftUI 不会等到向下滚动才展示它们,它只会立即创建它们,所以这样有时会占用内存。
如果想避免这种情况发生,可以采用一个替代方案,分别称为 LazyVStack
和 LazyHStack
。它们的使用方式与常规堆栈完全相同,但会按需加载其内容(在实际显示之前它们不会创建视图)。因此最大限度地减少了所使用的系统资源量。
ScrollView {
LazyVStack(spacing: 10) {
ForEach(0..<100) {
CustomText("Item \\($0)")
.font(.title)
}
}
.frame(maxWidth: .infinity)
}
VStack
默认只占用最小宽度,而惰性堆栈 LazyVStack
默认占据最大宽度LazyVStack
可以设置 alignment 和 spacing 参数来影响样式// 要在惰性堆栈中固定视图,需要使用到 Section 视图,并定义其页头页脚。例如:
ScrollView {
LazyVStack {
Section {
ForEach(O..<50) {
Text("Item \\($O)").font(.title)
}
} header: {
HeaderView()
} footer: {
FooterView()
}
}
这时可以使用 pinnedViews
参数,设置在滚动时固定某个视图了。该参数设置后,惰性堆栈中的每个 section 的指定视图都会在滑动时固定,只是本例中只有一个 section 。
// 指定 .sectionHeaders 可以在滑动时,让每个 section 的 Header 保持顶部固定
LazyVStack(pinnedViews: .sectionHeaders) {
Section {
ForEach(0..<50) {
Text("Item \\($0)").font(.title)
}
} header: {
HeaderView()
} footer: {
FooterView()
}
}
// 同理可以指定 .sectionFooters 固定每个 section 的 Footer
LazyVStack(pinnedViews: .sectionFooters) { ... }
// 还可以同时指定两个视图,固定两个视图
LazyVStack(pinnedViews: [.sectionHeaders, .sectionFooters]) { ... }
Divider 用于制作分割线… 如果它被放在 VStack 视图里,它会被渲染成横线;如果它被放在 HStack 视图里,它会被渲染成竖线
Divider()
// 设置分割线粗细
.frame(height: 1)
// 设置分割线颜色
.background(Color(red: 240/255, green: 240/255, blue: 240/255))
.padding(.horizontal)
Spacer 弹性空间属于扩张型视图,主要用于填充空白。但也可以通过设置精确控制它的尺寸大小:
// 设置准确的高度
Spacer().frame(height: 50)
// 还可以指定最小和最大范围
Spacer()..frame(minHeight: 50, maxHeight: 500)
// 还可以通过 minLength 设置最小长度
Spacer(minLength: 0)
如何设置平分布局:
在一个视图中添加 N 个 Spacer ,其原理是将这个视图中的可用空间,分成N份,给到每一个Spacer。可以利用这一点来精细控制间隔。
//例如,我们可以在顶部留出三分之一的空间,在底部留出三分之二的空间,如下所示:
VStack {
Spacer()
Text("First")
Text("Second")
Text("Third")
Spacer()
Spacer()
}