携手创作,一起生长!这是我参与「日新方案 8 月更文挑战」的第28天,点击查看活动概况

介绍

早在 2020 年,咱们就具有了在 SwiftUI(LazyVGrid 和 LazyHGrid)中制作网格的新视图控件。两年后,咱们又取得了另一种在网格(Grid)中显现视图的视图控件。可是,这些新增功能十分不同,不仅在您运用它的办法上,而且在它内部的行为办法上。 2020 年的观念很懒惰。这些新人很热心。

lazy grids不会烘托甚至实例化屏幕外的视图。单元格视图仅在它们被翻滚时创立,而且在它们翻滚时停止计算。

这篇文章的主题 Eager Grids 正好相反。 SwiftUI 不在乎它们是在屏幕上仍是在屏幕外。一切视图都被同等对待。这或许会出现很多单元的功能问题。可是,多少是一个很大的数字是一个不或许答复的问题。这将取决于您的单元格视图的杂乱性。

所以假如lazy grids表现更好,这就引出了一个问题,我为什么要运用Eager Grids?事实是,Eager Gridslazy grids更有优势,反之亦然。例如,Eager Grids支撑列跨过,而lazy grids不支撑。归根结底,功能并不是唯一需求考虑的要素。在本文中,咱们将探究这些新网格,以便您在挑选其间一个时做出明智的决议。

关于容器视图的一句话

在咱们开端探究 Grid 视图之前,让我先谈谈容器视图。也便是说,接纳视图构建器并以特定办法呈现其内容的视图(HStack、VStack、ZStack、Lazy*Grid、Group、List、ForEach 等)。请耐心等待,这将在今后有所帮助。

有两种类型的容器视图。我以为这些类型没有正式名称。我只会称它们为“有布局的容器”和“没有布局的容器”。用几个比如能够更好地解说这一点:

使用 SwiftUI 的 Eager Grids

struct ContentView: View {
    var body: some View {
        HStack {
            Group {
                Text("Hello")
                Text("World")
                Image(systemName: "network")
            }
            .padding(10)
            .border(.red)
        }
    }
}

同样能够这么写:

struct ContentView: View {
    var body: some View {
        HStack {
            Text("Hello")
                .padding(10)
                .border(.red)
            Text("World")
                .padding(10)
                .border(.red)
            Image(systemName: "network")
                .padding(10)
                .border(.red)
        }
    }
}

从示例中能够看出,Group 修饰符分别应用于每个包括的视图。此外,Group 视图自身没有供给任何布局,也没有任何自己的几许图形。一切布局都由其父级执行:HStack。

可是,具有布局的容器(例如 HStack)上的修饰符应用于容器,该容器确实具有自己的几许形状:

使用 SwiftUI 的 Eager Grids

struct ContentView: View {
    var body: some View {
        HStack {
            Text("Hello")
            Text("World")
            Image(systemName: "network")
        }
        .padding(10)
        .border(.red)
    }
}

您或许会问,当 Group 没有父级时会产生什么。这不是问题。当没有布局容器存在时,SwiftUI 会隐式运用 VStack。这便是为什么这也有效:

使用 SwiftUI 的 Eager Grids

struct ContentView: View {
    var body: some View {        
        Text("Hello")
        Text("World")
        Image(systemName: "network")
    }
}

另一个没有布局的容器示例是 ForEach

使用 SwiftUI 的 Eager Grids

struct ContentView: View {
    var body: some View {
        HStack {
            ForEach(0..<5) { idx in
                Text("\(idx)")
            }
            .padding(10)
            .border(.blue)
        }
    }
}

这与网格有什么关系?咱们将鄙人一节中找到答案。

咱们的榜首个网格

让咱们树立咱们的榜首个网格。语法十分简略。您运用 Grid 容器视图,然后经过对 GridRow 容器内的单元格视图进行分组来界说其行。

使用 SwiftUI 的 Eager Grids

struct ContentView: View {
    var body: some View {
        Grid {
            GridRow {
                Text("Cell #1")
                    .padding(20)
                    .border(.red)
                Text("Cell #2")
                    .padding(20)
                    .border(.red)
            }
            GridRow {
                Text("Cell #3")
                    .padding(20)
                    .border(.green)
                Text("Cell #4")
                    .padding(20)
                    .border(.green)
            }
        }
        .padding(10)
        .border(.blue)
    }
}

这便是咱们议论容器的当地。假如我告知你 Grid 是一个带有布局的容器,但 GridRow 不是。这意味着咱们能够重写咱们的代码并取得相同的成果:

struct ContentView: View {
    var body: some View {
        Grid {
            GridRow {
                Text("Cell #1")
                Text("Cell #2")
            }
            .padding(20)
            .border(.red)
            GridRow {
                Text("Cell #3")
                Text("Cell #4")
            }
            .padding(20)
            .border(.green)
        }
        .padding(10)
        .border(.blue)
    }
}

请留意,并非一切行都具有相同数量的单元格。虽然这儿的大多数示例都能够,但每一行能够包括恣意数量的单元格。

探究网格选项

在以下部分中,咱们将讨论不同的网格巨细、对齐和跨过选项。但为了让工作变得更简略,我创立了一个名为 Grid Trainer 的小应用程序。该应用程序可让您以交互办法运用一切这些网格参数。当您更改网格时,该应用程序还将向您显现生成您创立的网格的代码。

整个应用程序坐落一个 swift 文件中,因而只需几秒钟即可完成设置。只需创立一个新的 Xcode 项目,将 ContentView.swift 文件替换为此 gist 文件中的文件,就能够开端了。请留意,虽然我在规划应用程序时主要考虑了 macOS,但该应用程序在 iPad 上也能流畅运转。无需更改。

当您阅览以下部分时,最好运转 Grid Trainer 应用程序并测验您对网格的理解。试着看看你是否能够猜测当你改变参数时网格会做什么。每次你得到你所期望的不同成果时,你都会学到一些关于网格的新东西。假如你得到你所期望的,你会重申你现已知道的。

空间

与 HStack 和 VStack 相似,Grid 容器具有用于距离的笔直和水平参数。假如未指定,则将运用体系默认值。

使用 SwiftUI 的 Eager Grids

Grid(horizontalSpacing: 5.0, verticalSpacing: 15.0) {
    GridRow {
        Rectangle().fill(Color(white: 0.20).gradient)
        Rectangle().fill(Color(white: 0.40).gradient)
        Rectangle().fill(Color(white: 0.60).gradient)
        Rectangle().fill(Color(white: 0.80).gradient)
    }
    .frame(width: 50.0, height: 50.0)
    GridRow {
        Rectangle().fill(Color(white: 0.80).gradient)
        Rectangle().fill(Color(white: 0.60).gradient)
        Rectangle().fill(Color(white: 0.40).gradient)
        Rectangle().fill(Color(white: 0.20).gradient)
    }
    .frame(width: 50.0, height: 50.0)
}

列宽,行高

网格中的单元格是视图,视图会习惯父级供给的巨细。在这种情况下,父级是网格。通常,列与其间最宽的单元格相同宽。鄙人面的示例中,橙色列的宽度由第二行中最宽的单元格决议。身高也是如此。在示例中,第二行与行中最高的紫色单元格相同高。

使用 SwiftUI 的 Eager Grids

未界说巨细的单元

默认情况下,网格将为单元格供给尽或许多的空间。那么假如一个网格是由一个 Rectangle() 视图组成的,会产生什么呢?如您所知,没有结构修饰符的形状喜欢增加以填充父级供给的一切空间。在这种情况下,网格将增加以填充其父级供给的一切空间。

鄙人面的示例中,绿色单元格在其水平维度上不受限制,因而它运用了一切可用空间。网格尽或许地增加,绿色单元格填充空间。可是,蓝色单元格被结构修改器限制为 50.0 pt 宽度。虚线表示网格边界。

使用 SwiftUI 的 Eager Grids

struct ContentView: View {
    let dash = StrokeStyle(lineWidth: 1.0, lineCap: .round, lineJoin: .miter, dash: [5, 5], dashPhase: 0)
    var body: some View {
        HStack(spacing: 0) {
            Circle().fill(.yellow).frame(width: 30, height: 30)
            Grid(horizontalSpacing: 0) {
                GridRow {
                    RoundedRectangle(cornerRadius: 15.0)
                        .fill(.green.gradient)
                        .frame(height: 50)
                    RoundedRectangle(cornerRadius: 15.0)
                        .fill(.blue.gradient)
                        .frame(width: 50, height: 50)
                }
            }
            .overlay { Rectangle().stroke(style: dash) }
            Circle().fill(.yellow).frame(width: 30, height: 30)
        }
    }
}

到目前为止,没有什么太令人惊讶的。这与咱们从运用 HStack 容器的榜首天起就看到的行为相同。可是,Grids 在这儿为咱们供给了一个挑选。咱们能够让单元格防止让网格增加以取得额定的空间。例如,关于水平维度,单元格只会增加到与其列中最宽的单元格相同多的空间。这样的单元格在确认列宽方面没有任何作用。这是经过应用于相关单元格的 gridCellUnsizedAxes() 修饰符来完成的。它接纳一个 Axis.Set 值。它能够是 .horizontal、.vertical 或两者的组合:[.horizontal, .vertical]。这告知网格给定单元格挑选不要求额定空间的维度。

假如您还没有,现在是开端运用 Grid Trainer 应用程序并挑战您迄今为止的常识的好时机。

鄙人面的示例中,赤色单元格在水平轴上未调整巨细,使其仅与绿色单元格相同大。即使爸爸妈妈供给更多,红细胞也不会接受。

使用 SwiftUI 的 Eager Grids

Grid {
  GridRow {
    RoundedRectangle(cornerRadius: 5.0)
      .fill(.green.gradient)
      .frame(width: 160.0, height: 80.0)
    RoundedRectangle(cornerRadius: 5.0)
      .fill(.blue.gradient)
      .frame(width: 80.0, height: 80.0)
  }
  GridRow {
    RoundedRectangle(cornerRadius: 5.0)
      .fill(.red.gradient)
      .frame(height: 80.0)
      .gridCellUnsizedAxes(.horizontal)
    RoundedRectangle(cornerRadius: 5.0)
      .fill(.yellow.gradient)
      .frame(width: 80.0, height: 80.0)
  }
}

对齐道路

网格对齐

当单元格的视图小于可用空间时,对齐办法将取决于几个参数。榜首个要考虑的参数是 Grid(alignment: Alignment)。它影响网格中的一切单元格,除非被下一个参数之一掩盖。假如未指定,则默以为 .center。

使用 SwiftUI 的 Eager Grids

Grid(alignment: .topLeading) {
    GridRow {
        Rectangle().fill(.yellow.gradient)
            .frame(width: 50.0, height: 50.0)
        Rectangle().fill(.green.gradient)
            .frame(width: 100.0, height: 100.0)
    }
    GridRow {
        Rectangle().fill(.orange.gradient)
            .frame(width: 100.0, height: 100.0)
        Rectangle().fill(.red.gradient)
            .frame(width: 50.0, height: 50.0)
    }
}

行笔直对齐

您还能够运用 GridRow(alignment: VerticalAlignment) 指定行对齐办法。请留意,在这种情况下,对齐办法仅仅笔直的。此行中的单元格将结合 Grid 参数和 GridRow 参数。行的笔直对齐将优先于对齐的网格笔直组件。鄙人面的示例中,具有 .topTrailing 值的网格与 .bottom 笔直行值相结合,会导致第二行中的单元格以 .bottomTrailing 对齐。其他即将运用网格对齐办法(即 .topTrailing)。

使用 SwiftUI 的 Eager Grids

Grid(alignment: .topTrailing) {
    GridRow {
        Rectangle().fill(Color(white: 0.25).gradient)
            .frame(width: 120.0, height: 100.0)
        Rectangle().fill(Color(white: 0.50).gradient)
            .frame(width: 50.0, height: 50.0)
        Rectangle().fill(Color(white: 0.50).gradient)
            .frame(width: 120.0, height: 100.0)
    }
    GridRow(alignment: .bottom) {
        Rectangle().fill(Color(white: 0.25).gradient)
            .frame(width: 120.0, height: 100.0)
        Rectangle().fill(Color(white: 0.50).gradient)
            .frame(width: 50.0, height: 50.0)
        Rectangle().fill(Color(white: 0.50).gradient)
            .frame(width: 50.0, height: 50.0)
    }
    GridRow {
        Rectangle().fill(Color(white: 0.25).gradient)
            .frame(width: 120.0, height: 100.0)
        Rectangle().fill(Color(white: 0.50).gradient)
            .frame(width: 120.0, height: 100.0)
        Rectangle().fill(Color(white: 0.50).gradient)
            .frame(width: 50.0, height: 50.0)
    }
}

列水平对齐

除了指定笔直行对齐办法外,您还能够指定列水平对齐办法。与行对齐的情况相同,该值将与行笔直值和网格的对齐值合并。您运用修饰符 gridColumnAlignment() 指示列的对齐办法

留意:文档十分清楚。 gridColumnAlignment 只能在每列一个单元格中运用。否则行为未界说。

在以下示例中,您能够看到一切对齐组合:

单元格 (1,1):对齐顶部前导。 (网格对齐) 单元格 (1, 2):对齐的 topTrailing。 (网格对齐+列对齐) 单元格(2,1):对齐的底部前导(网格对齐+行对齐) 单元格 (2,2):对齐的底部尾随(网格对齐 + 行对齐 + 列对齐)

使用 SwiftUI 的 Eager Grids

struct ContentView: View {
    var body: some View {
        Grid(alignment: .topLeading, horizontalSpacing: 5.0, verticalSpacing: 5.0) {
            GridRow {
                CellView(color: .green, width: 80, height: 80)
                CellView(color: .yellow, width: 80, height: 80)
                    .gridColumnAlignment(.trailing)
                CellView(color: .orange, width: 80, height: 120)
            }
            GridRow(alignment: .bottom) {
                CellView(color: .green, width: 80, height: 80)
                CellView(color: .yellow, width: 80, height: 80)
                CellView(color: .orange, width: 80, height: 120)
            }
            GridRow {
                CellView(color: .green, width: 120, height: 80)
                CellView(color: .yellow, width: 120, height: 80)
                CellView(color: .orange, width: 80, height: 80)
            }
        }
    }
    struct CellView: View {
        let color: Color
        let width: CGFloat
        let height: CGFloat
        var body: some View {
            RoundedRectangle(cornerRadius: 5.0)
                .fill(color.gradient)
                .frame(width: width, height: height)
        }
    }
}

单元格对齐

最后,您还能够运用 .gridCellAnchor(_: anchor: UnitPoint) 修饰符为单元格指定独自的对齐办法。此对齐办法将掩盖给定单元格的任何网格、列和行对齐办法。留意参数类型不是Alignment,而是UnitPoint。这意味着除了运用预界说的点 .topLeading、.center 等之外,您还能够创立恣意点,例如 UnitPoint(x: 0.25, y: 0.75):

使用 SwiftUI 的 Eager Grids

Grid(alignment: .topTrailing) {
    GridRow {
        Rectangle().fill(.green.gradient)
            .frame(width: 120.0, height: 100.0)
        Rectangle().fill(.blue.gradient)
            .frame(width: 50.0, height: 50.0)
            .gridCellAnchor(UnitPoint(x: 0.25, y: 0.75))
    }
    GridRow {
        Rectangle().fill(.blue.gradient)
            .frame(width: 50.0, height: 50.0)
        Rectangle().fill(.green.gradient)
            .frame(width: 120.0, height: 100.0)
    }
}

文本基线对齐

除了常见的对齐办法,请记住您还能够运用文本基线对齐办法。关于 Grid 和 GridRow:

使用 SwiftUI 的 Eager Grids
Grid(alignment: .centerFirstTextBaseline) {
    GridRow {
        Text("Align")
        Rectangle()
            .fill(.green.gradient.opacity(0.7))
            .frame(width: 50, height: 50)
    }
}
.font(.system(size: 36))

没有 GridRow 的行

假如 Grid 在 GridRow 容器之外有一个视图,则它被用作跨过一切列的单个单元格行。这种类型的单元格的常见用处是创立分隔符。例如,您能够运用 Divider() 视图,或许更杂乱的视图,如下例所示。请留意,咱们通常不期望分隔线使网格增加到最大值,因而咱们使视图在水平轴上未调整巨细。这将使分隔线与最宽的行相同宽,但不会更宽。

使用 SwiftUI 的 Eager Grids

Grid(horizontalSpacing: 5.0, verticalSpacing: 5.0) {
    GridRow {
        RoundedRectangle(cornerRadius: 5.0).fill(.green.gradient)
        RoundedRectangle(cornerRadius: 5.0).fill(.purple.gradient)
        RoundedRectangle(cornerRadius: 5.0).fill(.blue.gradient)
    }
    .frame(width: 50.0, height: 50.0)
    Rectangle()
        .fill(LinearGradient(colors: [.gray, .clear, .gray], startPoint: .leading, endPoint: .trailing))
        .frame(height: 2.0)
        .gridCellUnsizedAxes(.horizontal)
    GridRow {
        RoundedRectangle(cornerRadius: 5.0).fill(.green.gradient)
        RoundedRectangle(cornerRadius: 5.0).fill(.purple.gradient)
        RoundedRectangle(cornerRadius: 5.0).fill(.blue.gradient)
    }
    .frame(width: 50.0, height: 50.0)
}

列跨过

Eager Grids优于Lazy Grids的长处之一是一切单元几许形状始终是已知的。这使得有一个跨过多列的单元格成为或许。要将单元格配置为跨过,请运用 .gridCellColumns(_ count: Int)

使用 SwiftUI 的 Eager Grids

Grid {
    GridRow {
        RoundedRectangle(cornerRadius: 5.0).fill(.green.gradient)
            .frame(width: 50.0, height: 50.0)
        RoundedRectangle(cornerRadius: 5.0).fill(.yellow.gradient)
            .frame(height: 50.0)
            .gridCellColumns(3)
            .gridCellUnsizedAxes(.horizontal)
        RoundedRectangle(cornerRadius: 5.0).fill(.purple.gradient)
            .frame(width: 50.0, height: 50.0)
    }
    GridRow {
        RoundedRectangle(cornerRadius: 5.0).fill(.green.gradient)
            .frame(width: 50.0, height: 50.0)
        RoundedRectangle(cornerRadius: 5.0).fill(.yellow.gradient)
            .frame(width: 50.0, height: 50.0)
        RoundedRectangle(cornerRadius: 5.0).fill(.orange.gradient)
            .frame(width: 50.0, height: 50.0)
        RoundedRectangle(cornerRadius: 5.0).fill(.red.gradient)
            .frame(width: 50.0, height: 50.0)
        RoundedRectangle(cornerRadius: 5.0).fill(.purple.gradient)
            .frame(width: 50.0, height: 50.0)
    }
}

留意歧义

考虑以下示例。咱们每行有 4 个单元格。除了榜首行的第二个单元格和第二行的第三个单元格之外,每个单元格都是 50.0 pt 宽。这些将尽或许地增加(不扩大网格)。这两个单元格也分别跨过两列。

struct ContentView: View {
    var body: some View {
        Grid(horizontalSpacing: 20.0, verticalSpacing: 20.0) {
            GridRow {
                CellView(width: 50.0, color: .green)
                CellView(color: .purple)
                    .gridCellColumns(2)
                CellView(width: 50.0, color: .blue)
                CellView(width: 50.0, color: .yellow)
            }
            .gridCellUnsizedAxes([.horizontal, .vertical])
            GridRow {
                CellView(width: 50.0, color: .green)
                CellView(width: 50.0, color: .purple)
                CellView(color: .blue)
                    .gridCellColumns(2)
                CellView(width: 50.0, color: .yellow)
            }
            .gridCellUnsizedAxes([.horizontal, .vertical])
        }
    }
    struct CellView: View {
        var width: CGFloat? = nil
        let color: Color
        var body: some View {
            RoundedRectangle(cornerRadius: 5.0)
                .fill(color.gradient)
                .frame(width: width, height: 50.0)
        }
    }
}

你以为应该产生什么?假如仔细看,这是“先有鸡仍是先有蛋的问题”。假如您查看榜首行中的第二个单元格,它应该跨过到以下列。可是第二行中的以下列应该扩展到第三列。那是什么?咱们能够满足一个条件或另一个条件,但不能一起满足这两个条件。这是因为榜首行查看第二行以确认下一列,而第二行查看榜首行以执行相同操作。 SwiftUI 需求以某种办法解决这个问题,假如你运转代码,你会得到以下成果:

使用 SwiftUI 的 Eager Grids

为了打破平局,一个简略的解决方案是添加第三行:

GridRow {
    CellView(width: 50, color: .green)
    CellView(width: 50, color: .purple)
    CellView(width: 50, color: .blue)
    CellView(width: 50, color: .yellow)
}

第三排打破平局,这便是它的姿态:

使用 SwiftUI 的 Eager Grids

假如您不需求第三行,则无论怎么都能够添加一个,但高度为零。不过,您或许仍需求处理距离。走运的是,这并不常见,但我会提到以防您遇到这种情况。

蜂窝再访

在文章 Impossible Grids 中,咱们是否探究了Lazy Grid,我写了一个示例,阐明怎么运用这些网格来呈现蜂窝中的单元格。创立这样的网格是测验网格或许的极限的好办法,所以我想我会重复这个操练,但这次运用Eager Grids

此gist file中供给了完整的工作网格。假如需求图片来测验代码,能够访问 this-person-does-not-exist.com。您能够下载带有随机面孔的不存在的人的方形图片!它们是人工智能生成的。 视频中运用的图片来自该网站。

从方形到六边形的过程

咱们必须从某个当地开端,所以咱们将创立一个方形图像网格,然后逐步添加代码将咱们的简略网格转换为蜂窝。

到现在为止,您应该具备完成转换所需的一切常识。我将为您供给一个起点和您需求执行的一系列过程,以便成功完成转换。可是,假如您没有时间,或许遇到困难,您能够查看上述 gist 文件中的代码。该代码有注释,指示它执行的每个过程的位置。

请留意,单元格的翻转并不是操练的一部分,但我也将其包括在要点中。

以下视频显现了起点以及它怎么变成蜂窝:

过程#1:咱们从方形图片网格开端。 过程#2:六边形没有 1:1 的尺度比。它的高度等于宽度 * cos(.pi/6)。假如您想知道原因,请查看 Impossible Grids,我在其间解说了原因。 过程#3:用供给的六边形取舍图像。 过程#4:将偶数行和奇数行移动到相对的两侧。偏移量是六边形宽度的一半 + 网格水平距离。 第 5 步:行需求堆叠,因而您需求将行高减少到四分之三 (3/4)。为什么是 3/4?,再次查看 Impossible Grids,我解说了原因。 第 6 步:要删除空白区域,请取舍网格边框(或将其放在 ScrollView 中,它会为您进行取舍)。 过程#7:假如使笔直距离等于水平距离,则单元格将均匀分布。

初始点

为了让你开端,这儿有一些代码。首先,咱们需求一些数据:

struct Person {
    let name: String
    let image: String
    var color: Color = .accentColor
    var flipped: Bool = false
}
class DataModel: ObservableObject {
    static let people: [Person] = [
        Person(name: "Peter", image: "image-1"),
        Person(name: "Carlos", image: "image-2"),
        Person(name: "Jennifer", image: "image-3"),
        Person(name: "Paul", image: "image-4"),
        Person(name: "Charlotte", image: "image-5"),
        Person(name: "Thomas", image: "image-6"),
        Person(name: "Sophia", image: "image-7"),
        Person(name: "Isabella", image: "image-8"),
        Person(name: "Ivan", image: "image-9"),
        Person(name: "Laura", image: "image-10"),
        Person(name: "Scott", image: "image-11"),
        Person(name: "Henry", image: "image-12"),
        Person(name: "Laura", image: "image-13"),
        Person(name: "Abigail", image: "image-14"),
        Person(name: "James", image: "image-15"),
        Person(name: "Amelia", image: "image-16"),
    ]
    static let colors: [Color] = [.yellow, .orange, .red, .purple, .blue, .pink, .green, .indigo]
    @Published var rows: [[Person]] = DataModel.buildDemoCells()
    var columns: Int { rows.first?.count ?? 0 }    
    var colCount: CGFloat { CGFloat(columns) }
    var rowCount: CGFloat { CGFloat(rows.count) }
    static func buildDemoCells() -> [[Person]] {
        var array = [[Person]]()
        // Add 7 rows
        for r in 0..<7 {
            var a = [Person]()
            // Add 6 cells per row
            for c in 0..<6 {
                let idx = (r*6 + c)
                var person = people[idx % people.count]
                person.color = colors[idx % colors.count]
                a.append(person)
            }
            array.append(a)
        }
        return array
    }
}

您还需求一个六边形:

使用 SwiftUI 的 Eager Grids

struct HexagonShape: Shape {
    func path(in rect: CGRect) -> Path {
        Path { path in
            let height = rect.height
            let width = rect.height * cos(.pi/6)
            let h = height / 4
            let w = width / 2
            let pt1 = CGPoint(x: rect.midX, y: rect.minY)
            let pt2 = CGPoint(x: rect.midX + w, y: h + rect.minY)
            let pt3 = CGPoint(x: rect.midX + w, y: h * 3 + rect.minY)
            let pt4 = CGPoint(x: rect.midX, y: rect.maxY)
            let pt5 = CGPoint(x: rect.midX - w, y: h * 3 + rect.minY)
            let pt6 = CGPoint(x: rect.midX - w, y: h + rect.minY)
            path.addLines([pt1, pt2, pt3, pt4, pt5, pt6])
            path.closeSubpath()
        }
    }
}

最后,你开端设置网格:

struct ContentView: View {
    @StateObject private var model = DataModel()
    private let cellWidth: CGFloat = 100
    private let cellHeight: CGFloat = 100
    var body: some View {
        VStack {
            Grid(alignment: .center, horizontalSpacing: 2, verticalSpacing: 2) {
                ForEach(model.rows.indices, id: \.self) { rowIdx in
                    GridRow {
                        ForEach(model.rows[rowIdx].indices, id: \.self) { personIdx in
                            let person = model.rows[rowIdx][personIdx]
                            Image(person.image)
                                .resizable()
                                .frame(width: cellWidth, height: cellHeight)
                        }
                    }
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.white)
    }
}

归纳

本年添加的 Grid 视图运用起来十分简略,而且添加到咱们现已具有的现有布局容器视图中。可是,本年还引入了一个新的布局协议,在将咱们的视图放置在屏幕上时,它供给了更多的挑选。咱们将在今后的文章中对此进行讨论。一起,我期望您喜欢这篇文章和 Grid 教练应用程序。