SwiftUI采用的布局方式是和Flutter一样是弹性布局,而不是iOS之前的坐标轴的方式布局,不用准确的设置出位置大小,只需要设置当前视图大小及视图间排布的方式。
Stack
SwiftUI里的三大容器视图(也是View),其他子视图(如Text、Image、Button等,也可以嵌套StackView)放在它们里面按一定的规则排序。容器视图的区域为所有子视图所占的矩形空间,如果只有一个子视图,那该容器大小就是这个子视图的大小。
子视图之间默认的
spacing=8(即不设置时候间距为8),子视图默认的padding=16(需要设置.padding()才会有)
Vstack
纵向布局容器,容器内子视图呈纵向排列,从上往下排列。
public struct VStack<Content> : View where Content : View {
public init(alignment: HorizontalAlignment = .center,
spacing: CGFloat? = nil,
@ViewBuilder content: () -> Content)
}
extension HorizontalAlignment {
public static let leading: HorizontalAlignment
public static let center: HorizontalAlignment
public static let trailing: HorizontalAlignment
}
在其初始化方法中可以设置在纵向排列时,子视图横向的对齐方式、间距。示例如下
VStack(alignment: .leading,
spacing: 10) {
Text("Text 0")
Text("Text 1")
Text("Text 2")
}
Hstack
横向布局容器,容器内子视图呈横向排列,从左往右排列。
public struct HStack<Content> : View where Content : View {
public init(alignment: VerticalAlignment = .center,
spacing: CGFloat? = nil,
@ViewBuilder content: () -> Content)
}
extension VerticalAlignment {
public static let top: VerticalAlignment
public static let center: VerticalAlignment
public static let bottom: VerticalAlignment
public static let firstTextBaseline: VerticalAlignment
public static let lastTextBaseline: VerticalAlignment
}
在其初始化方法中可以设置在横向排列时,子视图纵向的对齐方式、间距。示例如下
HStack(alignment: .top,
spacing: 10) {
Text("Text 0")
Text("Text 1")
Text("Text 2")
}
Zstack
深度布局容器,容器内子视图呈前后排列,从里到外排列(屏幕为参照)。
public struct ZStack<Content> : View where Content : View {
public init(alignment: Alignment = .center,
@ViewBuilder content: () -> Content)
}
extension Alignment {
public static let center: Alignment
public static let leading: Alignment
public static let trailing: Alignment
public static let top: Alignment
public static let bottom: Alignment
public static let topLeading: Alignment
public static let topTrailing: Alignment
public static let bottomLeading: Alignment
public static let bottomTrailing: Alignment
}
在其初始化方法中可以设置在横向排列时,每一层中子视图在整个坐标系里的对齐方式。示例如下
ZStack(alignment: .leading) {
Color.pink
Text("ZStack")
}
Spacer
一个看似透明的视图,通常在Stack布局中起重要作用,它起一个撑满的作用,比如Hstack中的一个Text想在屏幕左边,那么右边添加一个Spacer,就会将右边剩余部分撑满,Text就会被撑到左边。在Vstack中同样可以控制一个视图在纵向的位置。如果给它设置宽度或者高度,那效果也会不一样。
示例如下
HStack() {
Text("Text 0")
Text("Text 1")
Text("Text 2")
Spacer()
}
.background(Color.gray)
Divider()
SwiftUI中的表示分割线的一条线,在容器内以交叉轴方向做延伸(如在VStack中,Divider则为横向,若不在任何Stack中,默认横向),在不设置长度的情况下会撑满容器的最大可显示区域交叉轴。这样容器类的区域也会随着Divider去放大。当然也可以给它设置相应的宽或高来满足我们的需求。
示例如下
VStack() {
Text("Text 0")
Text("Text 1")
Divider()
Text("Text 2")
}
.background(Color.gray)

LazyStack
简单理解就是懒加载的Stack视图。比如把VStack替换成LazyVStack,发现只加载屏幕上需要展示的View,当滑动时才去展示更多的View,即触发了懒加载机制。当我们去掉ScrollView后,发现无法触发懒加载。当我们把ForEach替换成用Group包装的多个组后,也不能实现懒加载效果。 所以LazyStack想要触发懒加载机制,ScrollView及ForEach缺一不可。
struct LazyHStack<Content> : View where Content : View {
public init(alignment: VerticalAlignment = .center,
spacing: CGFloat? = nil,
pinnedViews: PinnedScrollableViews = .init(),
@ViewBuilder content: () -> Content)
}
public struct PinnedScrollableViews : OptionSet {
public static let sectionHeaders: PinnedScrollableViews
public static let sectionFooters: PinnedScrollableViews
}
可以看到在其初始化方法中,比普通HStack多了个PinnedScrollableViews类型参数,这是一组视图类型,可以固定在滚动视图的范围。默认为空。也可以通过设置固定为标头、尾。
绝对位置、相对位置
-
绝对位置:
.position(x:,y:)- 设置视图的中心点距离左上角(0,0)的位置。
-
相对位置:
.offset()- 相对position来说不会新建一个父视图,而是直接将中心点按offset所标大小移动。只是去改变显示的位置。
Overlay
iOS 15 available
在实现深度层级顺序的功能,布局上除了ZStack,我们还可以使用overlay。
extension View {
public func overlay<V>(alignment: Alignment = .center,
@ViewBuilder content: () -> V) -> some View where V : View}
// 不多赘述了,上示例,使用上也是很方便的
Color.pink
.overlay(alignment: .center) {
Text("Text")
}