SwiftUI学习笔记-布局

645 阅读4分钟

SwiftUI采用的布局方式是和Flutter一样是弹性布局,而不是iOS之前的坐标轴的方式布局,不用准确的设置出位置大小,只需要设置当前视图大小及视图间排布的方式。

Stack

SwiftUI里的三大容器视图(也是View),其他子视图(如TextImageButton等,也可以嵌套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")
  }