SwiftUI

布局基础

SwiftUI 提供了一套声明式的布局系统,通过组合不同的容器视图(Container Views)来构建复杂的用户界面。本文详细总结了 SwiftUI 布局基础的核心概念和组件。

堆栈视图 (Stack Views)

堆栈视图是 SwiftUI 布局的基础,用于在水平、垂直或深度(Z 轴)方向上排列视图。

HStack, VStack, ZStack

  • HStack: 水平排列子视图。
  • VStack: 垂直排列子视图。
  • ZStack: 沿 Z 轴叠加子视图(后添加的视图在顶层)。
struct StackExample: View {
    var body: some View {
        VStack(spacing: 20) {
            // 水平排列
            HStack {
                Text("Leading")
                Spacer()
                Text("Trailing")
            }
            
            // 叠加排列
            ZStack {
                Circle().fill(Color.blue).frame(width: 50, height: 50)
                Text("1").foregroundColor(.white)
            }
        }
    }
}

常用修饰符

  • alignment: 设置子视图在堆栈中的对齐方式(如 .leading, .center, .top)。
  • spacing: 设置子视图之间的间距。

懒加载堆栈 (Lazy Stacks)

当需要展示大量数据时,使用标准堆栈会一次性加载所有视图,影响性能。懒加载堆栈仅在视图即将出现在屏幕上时才进行渲染。

  • LazyHStack: 水平方向的懒加载堆栈,通常配合 ScrollView(.horizontal) 使用。
  • LazyVStack: 垂直方向的懒加载堆栈,通常配合 ScrollView 使用。
ScrollView {
    LazyVStack(alignment: .leading) {
        ForEach(1...100, id: \.self) { item in
            Text("Row \(item)")
        }
    }
}

网格布局 (Grids)

SwiftUI 提供了两种网格布局方式:静态网格 (Grid) 和懒加载网格 (LazyGrid)。

Grid (iOS 16+)

Grid 是一个容器视图,用于创建二维布局。它会立即加载所有子视图,适合视图数量较少且需要精确对齐的场景。使用 GridRow 定义行。

Grid {
    GridRow {
        Text("Top Left")
        Text("Top Right")
    }
    Divider()
    GridRow {
        Text("Bottom Left")
        Text("Bottom Right")
    }
}
  • GridRow: 代表网格中的一行。
  • gridCellColumns(_:): 让某个单元格跨越多列。

LazyVGrid & LazyHGrid

适用于大量数据的网格布局,仅渲染可见区域的单元格。需要配合 GridItem 定义列(或行)的布局行为。

  • GridItem: 定义网格的列(对于 LazyVGrid)或行(对于 LazyHGrid)。支持 .fixed, .flexible, .adaptive 三种尺寸模式。
struct LazyGridExample: View {
    let columns = [
        GridItem(.flexible()),
        GridItem(.flexible())
    ]

    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns, spacing: 20) {
                ForEach(0..<20) { index in
                    Text("Item \(index)")
                        .frame(height: 50)
                        .background(Color.gray.opacity(0.2))
                }
            }
        }
    }
}

弹性空间与分割线

Spacer

Spacer 是一个灵活的空间视图,它会沿着堆栈的主轴方向尽可能地扩展,从而推挤其他视图。

  • HStack 中使用 Spacer 可以将视图推向两端。
  • minLength: 可以指定最小长度。

Divider

Divider 是一条细线,用于在视觉上分隔内容。

  • HStack 中是垂直线。
  • VStack 中是水平线。

几何读取器 (GeometryReader)

GeometryReader 是一个容器视图,它将其自身的大小和坐标空间作为 GeometryProxy 传递给闭包。这使得子视图可以根据父视图的大小进行动态布局。

GeometryReader { geometry in
    HStack(spacing: 0) {
        Text("Left")
            .frame(width: geometry.size.width * 0.5)
            .background(Color.green)
        Text("Right")
            .frame(width: geometry.size.width * 0.5)
            .background(Color.orange)
    }
}
.frame(height: 50)

GeometryReader 默认会尽可能占用所有可用空间,可能会影响父视图的布局。

自适应布局 (ViewThatFits)

ViewThatFits (iOS 16+) 允许你提供一组视图,它会自动选择第一个能够完整放入可用空间的视图。这对于适配不同屏幕尺寸或动态内容非常有用。

ViewThatFits {
    // 优先尝试水平排列(如果宽度足够)
    HStack {
        Text("Long Text Content")
        Text("More Content")
    }
    
    // 如果宽度不够,回退到垂直排列
    VStack {
        Text("Long Text Content")
        Text("More Content")
    }
}

自定义布局 (Layout Protocol)

对于标准容器无法满足的复杂布局需求,SwiftUI 提供了 Layout 协议 (iOS 16+)。通过实现 sizeThatFitsplaceSubviews 方法,可以完全自定义视图的排列逻辑。这是一个高级特性,通常用于构建通用的布局容器。

总结

  • 使用 Stacks (HStack, VStack, ZStack) 处理基本布局。
  • 使用 Lazy Stacks 处理长列表性能优化。
  • 使用 Grid 处理静态二维对齐,使用 LazyGrid 处理大量数据的网格。
  • 使用 SpacerDivider 控制间距和分隔。
  • 使用 GeometryReader 获取尺寸信息。
  • 使用 ViewThatFits 实现内容自适应。
在 GitHub 上编辑

上次更新于