轻学编程|Swift 教程 017:循环和列表
轻学编程|Swift 教程 017:循环和列表

轻学编程|Swift 教程 017:循环和列表

这节课,我们将学习 Swift 中的 `for` 循环,以及 SwiftUI 中的 `ForEach`。

它们都可以帮助我们按顺序读取数组中的元素。在实际开发中,我们经常需要把一组数据一个个取出来,再进行处理或显示,而循环正是完成这件工作的重要工具。

当我们浏览网页或使用 App 时,经常会看到很多列表或条目。这样的内容通常会先存储在 `数组` 中,然后再通过 `循环` 依次读取出来,最后显示在界面上。

例如,Google 的搜索结果列表:

搜索结果列表中的每一个条目,通常都对应一项数据,通过循环依次显示出来。

因此,`数组` 和 `循环` 经常会一起出现。`数组` 负责保存一组数据,`循环` 负责把这些数据按顺序取出来,并执行相同的操作。

下面,我们先从最基础的 `for` 循环开始。

倒计时

假设我们要模拟一个倒计时功能,依次输出 `“5、4、3、2、1”`。

最直接的写法是:

print(5)
print(4)
print(3)
print(2)
print(1)

这种方式虽然可以模拟倒计时功能,但如果内容很多,就会显得非常重复,而且后续也不方便修改。

这时,我们可以先把数据放进数组,再使用 `for` 循环依次输出:

let countDown = [5, 4, 3, 2, 1]

for i in countDown {
    print(i)
}

这种写法更简洁,也更容易扩展。即使倒计时中的数字有很多,我们也不需要一行一行重复写 `print`,只需要让循环自动处理数组中的每一个元素即可。

我们可以把这段代码放到按钮中测试:

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Button("for") {
                let countDown = [5, 4, 3, 2, 1]

                for i in countDown {
                    print(i)
                }
            }
        }
    }
}

当点击按钮时,`for` 循环会按照数组中的顺序,依次取出 `countDown` 里的每一个整数,并将它们打印到控制台。

接下来,我们看一下 `for` 循环是如何工作的。

for 循环

`for` 循环用于按顺序处理一组数据,最常见的对象是数组和范围。

基本写法:

for 元素 in 数组或范围 {
    执行的代码
}

例如,刚才的倒计时代码:

let countDown = [5, 4, 3, 2, 1]

for i in countDown {
    print(i)
}

这里的 `i` 表示当前取出的元素,`in` 后面跟的是要循环的数据,也就是 `countDown` 数组。

这段代码的含义是:程序会从 `countDown` 数组中按顺序取出每一个元素。每取出一个元素,就暂时用 `i` 来表示,然后执行大括号中的代码。

执行过程可以理解为:

  • 第一次取出的是 `5`,所以这时 `i` 的值是 `5`
  • 执行 `print(i)`,输出 `5`
  • 然后继续取出下一个元素 `4`
  • 再执行一次 `print(i)`,输出 `4`

后面的 `3`、`2`、`1` 也会按照同样的方式依次执行。

当数组中的所有元素都处理完成后,`for` 循环就会自动结束。

我们可以借助 `for` 循环获取数组中的每一个元素,并完成像倒计时这样的功能。

提示:`i` 是 for 循环中很常见的命名方式,用来表示当前取出的元素,但它不是固定写法,也可以使用其他英文名称。

例如,使用 `num` 表示每一个取出的元素:

for num in countDown {
    print(num)
}

在大括号内,也需要使用 `num` 来表示当前取出的元素。

循环范围

`for` 循环不仅可以循环数组,还可以循环范围。

例如,当我们需要计算 `50` 以内正整数的总和时,就需要把 `1` 到 `50` 之间的所有整数依次相加。

最直接的写法可能是:

let count = 1 + 2 + 3 + ... + 49 + 50
print(count)

这个写法的问题在于,我们需要把每一个数字都手动写出来,既麻烦,也不适合真正的开发场景。

这时,就可以使用范围运算符配合 `for` 循环来完成。

在前面的“运算符”章节中,我们已经学习过范围运算符:

1...3   // 1, 2, 3

其中,`…` 表示闭区间,也就是包含起点和终点。

因此,这里可以使用 `for` 循环 `1` 到 `50` 的整数范围:

for i in 1...50 {
    
}

我们需要把这些整数相加,所以还需要一个变量来保存总和:

var count = 0

然后在每次循环时,把当前取出的数字加到 `count` 上:

var count = 0
for i in 1...50 {
    count += i
}
print(count)	// 1275

这段代码表示:

  • `count` 用来保存当前的总和
  • `for` 循环会从 `1` 到 `50` 依次取出整数
  • 每次取出一个整数,就执行一次 `count += i`
  • 循环结束后,`count` 中保存的就是最终总和。

这样,我们就可以得到 `50` 以内所有正整数总和。

同理,如果我们需要计算 `1` 到 `100` 的整数总和,只需要修改范围即可:

var count = 0
for i in 1...100 {
    count += i
}
print(count)	// 5050

`for` 循环不仅可以循环数组,还可以循环一个范围。这种按顺序重复处理数据的能力,在开发中非常常见。

它可以帮助我们自动处理很多重复性的工作,而不需要手动编写每一个数字的相加过程,或者其他类似的重复操作。

SwiftUI 中的循环

前面的 `for` 循环属于 Swift 语言。

但是在 SwiftUI 中,我们不能直接在视图结构里使用 `for` 来生成视图:

VStack {
    for i in 1...100 {    // Error
        count += i
    }
}

这是因为 `VStack` 里面需要的是 SwiftUI 视图,而普通的 `for` 循环本身并不是一个视图。

如果我们想在 SwiftUI 中根据数组的内容重复生成界面,就需要使用 `ForEach`。

例如,我们要显示一个小组内所有成员的名字,可以写为:

VStack {
    Text("Sam")
    Text("John")
    Text("Wathon")
    Text("Bob")
}

显示效果:

然后,我们为每一个 `Text` 添加相同的修饰符:

VStack {
    Text("Sam")
        .font(.footnote)
        .fontWeight(.bold)
        .foregroundStyle(Color.black)
    Text("John")
        .font(.footnote)
        .fontWeight(.bold)
        .foregroundStyle(Color.black)
    Text("Wathon")
        .font(.footnote)
        .fontWeight(.bold)
        .foregroundStyle(Color.black)
    Text("Bob")
        .font(.footnote)
        .fontWeight(.bold)
        .foregroundStyle(Color.black)
}

现在,每一个 `Text` 都显示为更小、更粗的字体。

显示效果:

虽然修饰符让界面更美观,但也让代码变得更长、更重复。

如果名字继续增加,或者后续还要统一修改字体、颜色等样式,就需要重复编写和修改很多相同的代码,这样会比较麻烦,也不方便维护。

这时,我们就可以把这些名字先放进数组,再使用 SwiftUI 中的 `ForEach`,根据数组中的数据重复生成视图:

VStack {
    let names = ["Sam", "John", "Wathon", "Bob"]
    ForEach(names, id:\.self) { item in
        Text(item)
            .font(.footnote)
            .fontWeight(.bold)
            .foregroundStyle(Color.black)
    }
}

这组代码更简洁,`ForEach` 会依次读取数组中的每一个元素,并把对应内容显示到界面上。

当我们后续新增名称时,只需要修改 `names` 数组中的名称,不需要每次都重复添加 `Text` 和复杂的修饰符。

ForEach

`ForEach` 是 SwiftUI 中用来重复生成视图的结构。可以把它理解为 SwiftUI 中一种类似 `for` 循环的结构。

基本用法:

ForEach(数组, id: \.self) { 元素 in
    SwiftUI 代码
}

例如,刚才的小组名称:

let names = ["Sam", "John", "Wathon", "Bob"]
ForEach(names, id:\.self) { item in
    Text(item)
        .font(.footnote)
        .fontWeight(.bold)
        .foregroundStyle(Color.black)
}

这段代码表示:

`names` 是循环的数组,也就是要显示的数据。

`id:\.self` 表示使用数组中的元素本身,来区分每一个元素。

在当前示例中,数组里保存的是字符串:

["Sam", "John", "Wathon", "Bob"]

所以 SwiftUI 会直接使用 “Sam”、”John”、”Wathon”、”Bob” 这些字符串本身,来区分不同的内容。

对于初学阶段常见的 String、Int 这类简单数组,我们通常都可以先写成:

id: \.self

现在只需要先记住这一种写法即可,不需要深入了解更多 id 的规则。

`item in` 中的 `item` 表示当前取出的元素。

例如,第一次循环时,`item` 是 `”Sam”`,第二次循环时,`item` 是 `”John”`。

因此:

Text(item)

就会依次变成:

Text("Sam")
Text("John")
Text("Wathon")
Text("Bob")

所以,这段代码的作用就是:从 `names` 数组中依次取出每一个名字,并为每一个名字生成一个 `Text` 视图。

执行过程

执行过程可以理解为:

  • `ForEach` 先读取 `names` 数组中的第一个元素 `”Sam”`
  • `item` 临时表示 `”Sam”`
  • `Text(item)` 显示 `”Sam”`
  • 然后继续读取下一个元素 `”John”`
  • 再重复执行同样的视图代码

最后,数组中的所有名字都会显示出来。

这样写的好处是:当名称变多时,我们不需要重复写很多个 `Text`,只需要修改数组内容即可。

循环照片

`ForEach` 不仅可以显示文字,也可以显示图片、颜色,或者其他 SwiftUI 视图。

例如,我们准备 4 张图片:

430F9BEF.jpeg
193E0C8C.jpeg
FEE2AB8D.jpeg
35AA4D15.jpeg

把这四张图片拖入 Xcode 的 Assets 文件夹中。

如果不使用 `ForEach`,我们需要把每一张图片都手动写出来:

struct ContentView: View {
    var body: some View {
        VStack {
            Image("430F9BEF")
                .resizable()
                .scaledToFit()
                .frame(width: 200)
            Image("193E0C8C")
                .resizable()
                .scaledToFit()
                .frame(width: 200)
            Image("FEE2AB8D")
                .resizable()
                .scaledToFit()
                .frame(width: 200)
            Image("35AA4D15")
                .resizable()
                .scaledToFit()
                .frame(width: 200)
        }
    }
}

显示效果:

这种写法虽然可以实现功能,但代码会比较长。

如果图片越来越多,就要重复写越来越多的 `Image` 代码。

后续如果想要统一修改图片大小,也需要一张一张去修改。

这时,就可以把图片名称放进数组,再使用 `ForEach` 来重复生成图片视图:

VStack {
    let images = ["430F9BEF","193E0C8C","FEE2AB8D","35AA4D15"]
    ForEach(images, id: \.self) {
        item in
        Image(item)
            .resizable()
            .scaledToFit()
            .frame(width: 200)
    }
}

这里的 `images` 是一个字符串数组,数组中的每一个元素都是图片名称。

`ForEach(images, id: \.self)` 表示,依次读取 `images` 数组中的每一个元素,使用元素本身来区分每一项,再根据当前元素生成对应的视图。

例如,第一次读取到的是 `”430F9BEF”`,那么:

Image(item)

就相当于:

Image("430F9BEF")

后面的图片也会按照同样的方式依次显示出来。

这里仍然使用:

id: \.self

原因和前面一样,因为 `images` 数组中的元素也是简单的字符串类型,所以初学阶段直接使用元素自身作为区分方式即可。

可以简单的理解为一句固定公式:

`ForEach` 循环的是 `String``Int` 这类简单数组时,通常写 `id: \.self`

新增图片

如果后续需要新增图片,只需要修改数组内容即可:

let images = ["430F9BEF","193E0C8C","FEE2AB8D","35AA4D15","430F9BEF","193E0C8C","FEE2AB8D","35AA4D15"]

不需要再重复编写新的 `Image` 代码。

如果图片比较多,可以配合 `ScrollView` 一起使用,否则内容超出屏幕后,就无法完整预览。

ScrollView {
    let images = ["430F9BEF","193E0C8C","FEE2AB8D","35AA4D15"]
    ForEach(images, id: \.self) {
        item in
        Image(item)
            .resizable()
            .scaledToFit()
            .frame(width: 200)
    }
}

修改图片

当我们需要统一修改图片样式时,只需要修改 `ForEach` 内部的代码即可。

例如,把宽度改成 `80`:

ForEach(images, id: \.self) {  item in
    Image(item)
        .resizable()
        .scaledToFit()
        .frame(width: 80)
}

这样,所有图片都会一起修改,不需要一张一张去改。

这就是 `ForEach` 的一个重要作用:

当多个视图结构相同、只有内容不同时,可以只写一份视图代码,再让数组中的数据决定显示内容。

总结

本节课,我们学习了 `for` 循环和 `ForEach`。

`for` 循环用于按顺序处理数组或范围中的元素,更倾向于处理数据。

`ForEach` 用于在 SwiftUI 中根据数组内容重复生成视图,更偏向视图显示。

对于当前初学阶段,`ForEach` 最常见的写法就是:

ForEach(数组, id: \.self) { 元素 in
    SwiftUI 代码
}

其中,`id: \.self` 表示使用元素本身来区分每一个元素。

当数组中的内容是 `String`、`Int` 这类简单类型时,我们通常都可以先使用这一个写法。

以后,在浏览网页或 App 时,如果看到一组重复的文字、图片或者其他列表内容时,可以想到这些内容在 SwiftUI 中,通常是先放进数组里,再通过 `ForEach` 依次显示出来。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注